The following are the necessary R-packages for running the code below:

library(tidyverse)
library(readr)
library(RColorBrewer)
# be sure to run install.packages("BiocManager") and use BiocManager::install("package") order to install and run the following libraries 
library(DESeq2)
library(apeglm)
library(ashr)
library(pheatmap)

1) Peparing Count Data

1/20/25 Below is a snippet of feature count data of Arabidopsis (both control and treated) regarding heavy metals.
GeneID Length Col01_1_1 Col01_1_2 Col01_1_3 Col01_2_1 Col01_2_2 Col01_2_3 Col01_3_1 Col01_3_2 Col01_3_3 Col01_4_1 Col01_4_2 Col01_4_3 Col01_5_1 Col01_5_2 Col01_5_3 ST35_1_1 ST35_1_2 ST35_1_3 ST35_2_1 ST35_2_2 ST35_2_3 ST35_3_1 ST35_3_2 ST35_3_3 ST35_4_1 ST35_4_2 ST35_4_3 ST35_5_1 ST35_5_2 ST35_5_3
AT1G01010.1 1688 179 242 121 160 247 188 398 601 373 568 820 855 586 800 892 334 152 90 350 396 288 576 577 786 613 712 777 927 936 972
AT1G01020.1 1623 0 0 0 0 0 0 1 0 1 1 1 0 2 1 4 0 0 0 0 0 0 1 0 0 2 3 0 0 6 0
AT1G01020.2 1085 15 8 4 23 3 10 4 2 7 8 13 10 9 5 19 19 5 5 6 6 6 23 12 15 13 13 5 10 12 11
AT1G01030.1 1905 131 100 111 72 78 72 94 88 96 80 93 96 52 85 113 119 81 93 75 78 77 93 116 119 72 80 80 70 100 123
AT1G01040.1 6251 148 90 99 91 128 85 149 147 156 189 212 188 154 214 178 178 121 125 109 84 142 176 156 189 186 160 227 207 243 209
AT1G01040.2 5877 5 5 7 8 8 15 9 17 13 23 20 26 16 20 14 16 11 6 19 10 21 12 10 23 31 27 35 34 24 13
AT1G01046.1 207 60 42 29 55 39 32 54 28 40 32 27 38 28 25 55 79 37 72 67 51 54 53 39 73 54 34 27 18 54 60
AT1G01050.1 976 2117 2075 1767 1775 1972 2081 1944 2000 1836 1588 2382 2225 1616 2045 2335 2428 1743 2118 2664 2345 2029 2272 2362 2716 1961 2034 2190 2285 2577 2346
AT1G01060.1 2704 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
AT1G01060.2 2814 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

I created a data frame that matches the sample info provided by Sanju’s PowerPoint slide as shown here:

The following code creates the data frame itself, which will be used as colData in DESeq2.

# extract column names for the samples (exclude non-sample columns)
sample_names <- colnames(feature_counts)[-c(1, 2)]  # "GeneID" and "Length" are the first two columns (non-sample columns)

# map sample names treatment and genotype columns, respectively 
colData <- data.frame(
  sample_name = sample_names,
  treatment = case_when(
    grepl("^Col01_1", sample_names) ~ "0",
    grepl("^Col01_2", sample_names) ~ "20",
    grepl("^Col01_3", sample_names) ~ "40",
    grepl("^Col01_4", sample_names) ~ "60",
    grepl("^Col01_5", sample_names) ~ "80",
    grepl("^ST35_1", sample_names) ~ "0",
    grepl("^ST35_2", sample_names) ~ "20",
    grepl("^ST35_3", sample_names) ~ "40",
    grepl("^ST35_4", sample_names) ~ "60",
    grepl("^ST35_5", sample_names) ~ "80",
    TRUE ~ NA_character_ # default if no pattern matches
    ),
  genotype = case_when(
    grepl("Col", sample_names, ignore.case = TRUE) ~ "wild_type",
    grepl("ST35", sample_names, ignore.case = TRUE) ~ "knockout_line",
    TRUE ~ NA_character_ # default if no pattern matches
    ),
  row.names = sample_names # set sample_name column content as row names
  )

# combine 'genotype' and 'treatment' into a new 'group' column with shortened genotypes
colData$group <- paste(
  case_when(
    colData$genotype == "wild_type" ~ "wt", 
    colData$genotype == "knockout_line" ~ "ko",
    TRUE ~ NA_character_
  ),
  colData$treatment,
  sep = "_"
)

# convert appropriate columns into factors and remove sample_name column 
colData <- colData |>
  mutate(
    treatment = factor(treatment),
    genotype = factor(genotype),
    group = factor(group)
    ) |>
  select(-sample_name) # remove sample_name column 

# verify data structure 
str(colData)
treatment genotype group
Col01_1_1 0 wild_type wt_0
Col01_1_2 0 wild_type wt_0
Col01_1_3 0 wild_type wt_0
Col01_2_1 20 wild_type wt_20
Col01_2_2 20 wild_type wt_20
Col01_2_3 20 wild_type wt_20
Col01_3_1 40 wild_type wt_40
Col01_3_2 40 wild_type wt_40
Col01_3_3 40 wild_type wt_40
Col01_4_1 60 wild_type wt_60

Let’s verify that the row names in colData matches the column names in feature_counts and are in the same order.

# checking for matching names 
all(colnames(feature_counts) %in% rownames(colData))
## [1] FALSE

The result is FALSE; this is due to the initial two columns found in the feature_counts for GeneID and Length. Let’s remove the Length column and convert the GeneID column into the row names of this data frame (it shouldn’t interfere with the DESeq2 output)!

# converting GeneID to rownames and removing Length column found in original feature count data set 
data_counts <- feature_counts |>
  column_to_rownames("GeneID") |>
  select(-Length)

# checking for matching names 
all(colnames(data_counts) %in% rownames(colData))
## [1] TRUE
# checking that they are in the same order
all(colnames(data_counts) == rownames(colData))
## [1] TRUE
# view new data counts format
head(data_counts, 10)
Col01_1_1 Col01_1_2 Col01_1_3 Col01_2_1 Col01_2_2 Col01_2_3 Col01_3_1 Col01_3_2 Col01_3_3 Col01_4_1 Col01_4_2 Col01_4_3 Col01_5_1 Col01_5_2 Col01_5_3 ST35_1_1 ST35_1_2 ST35_1_3 ST35_2_1 ST35_2_2 ST35_2_3 ST35_3_1 ST35_3_2 ST35_3_3 ST35_4_1 ST35_4_2 ST35_4_3 ST35_5_1 ST35_5_2 ST35_5_3
AT1G01010.1 179 242 121 160 247 188 398 601 373 568 820 855 586 800 892 334 152 90 350 396 288 576 577 786 613 712 777 927 936 972
AT1G01020.1 0 0 0 0 0 0 1 0 1 1 1 0 2 1 4 0 0 0 0 0 0 1 0 0 2 3 0 0 6 0
AT1G01020.2 15 8 4 23 3 10 4 2 7 8 13 10 9 5 19 19 5 5 6 6 6 23 12 15 13 13 5 10 12 11
AT1G01030.1 131 100 111 72 78 72 94 88 96 80 93 96 52 85 113 119 81 93 75 78 77 93 116 119 72 80 80 70 100 123
AT1G01040.1 148 90 99 91 128 85 149 147 156 189 212 188 154 214 178 178 121 125 109 84 142 176 156 189 186 160 227 207 243 209
AT1G01040.2 5 5 7 8 8 15 9 17 13 23 20 26 16 20 14 16 11 6 19 10 21 12 10 23 31 27 35 34 24 13
AT1G01046.1 60 42 29 55 39 32 54 28 40 32 27 38 28 25 55 79 37 72 67 51 54 53 39 73 54 34 27 18 54 60
AT1G01050.1 2117 2075 1767 1775 1972 2081 1944 2000 1836 1588 2382 2225 1616 2045 2335 2428 1743 2118 2664 2345 2029 2272 2362 2716 1961 2034 2190 2285 2577 2346
AT1G01060.1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
AT1G01060.2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

As we can see, it now returns TRUE for both conditions (matching names and same order).

2) Construct a DESeqDataSet Object

1/21/25

We will now be creating our DESeqDataSet object using the DESeq2 package and our recently updated and newly created data frames titled data_counts and colData, respectively.

# creating DESeq2 object 
dds <- DESeqDataSetFromMatrix(countData = data_counts,
                                   colData = colData,
                                   design = ~ treatment + genotype + treatment:genotype)

dds 
## class: DESeqDataSet 
## dim: 40745 30 
## metadata(1): version
## assays(1): counts
## rownames(40745): AT1G01010.1 AT1G01020.1 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(30): Col01_1_1 Col01_1_2 ... ST35_5_2 ST35_5_3
## colData names(3): treatment genotype group

We have a total of 40745 rows.

Let’s now pre-filter the dds to remove those entries that have less than 10 read counts (this is an arbitrary number I’ve simply chosen according to Dr. Liang’s advice).

# remove low gene counts (<10)
keep_counts <- rowSums(counts(dds)) >= 10
dds <- dds[keep_counts,]

dds
## class: DESeqDataSet 
## dim: 27710 30 
## metadata(1): version
## assays(1): counts
## rownames(27710): AT1G01010.1 AT1G01020.1 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(30): Col01_1_1 Col01_1_2 ... ST35_5_2 ST35_5_3
## colData names(3): treatment genotype group

We have filtered down to 27710 rows.

3) Re-level Factors for Treatment Comparisons

1/21/25

We now want to re-level and compare different treatment conditions to one another. Here are the following variations of the comparisons we will conduct:

  • wild_type control vs. knockout_line control
    • 0 vs. 0
  • wild_type treatment vs. wild_type control
    • WT_20 vs. WT_0, WT_40 vs. WT_0, etc.
  • knockout_line treatment vs. knockout_line control
    • KO_20 vs. KO_0, KO_40 vs. KO_0, etc.
  • knockout_line treatment vs. wild_type treatment
    • KO_20 vs. WT_20, KO_40 vs. WT_40, etc.

Note: we don’t have to necessarily state which reference level we want to use. DESeq2 will simply choose alphabetically which level to use.

# use wild_type control as reference baseline 
dds$treatment <- relevel(dds$treatment, ref = "0")
dds$genotype <- relevel(dds$genotype, ref = "wild_type")
# use the control as reference for every single dds object I will be making 

# you may want to set this to a different variable object so that we can keep reusing our set up data and not overwrite it every time (double-check with Dr. Liang if I can do that because this doesn't seem t make new variable anyway)

IMPORTANT NOTE: Make sure to collapse the technical replicates before analysis; however, do not collapse biological replicates! (I don’t really know what this means though)

4) Run DESeq2

1/21/25

We can now run DESeq2 and see if our data structure works!

# run the differential gene expression analysis
dds <- DESeq(dds)

# view what conditions were contrasted to make sure they match what we actually meant to compare 
resultsNames(dds)
##  [1] "Intercept"                           "treatment_20_vs_0"                  
##  [3] "treatment_40_vs_0"                   "treatment_60_vs_0"                  
##  [5] "treatment_80_vs_0"                   "genotype_knockout_line_vs_wild_type"
##  [7] "treatment20.genotypeknockout_line"   "treatment40.genotypeknockout_line"  
##  [9] "treatment60.genotypeknockout_line"   "treatment80.genotypeknockout_line"
# store the results in an object 
res_dds <- results(dds)

# view DESeq2 results 
res_dds
## log2 fold change (MLE): treatment80.genotypeknockout line 
## Wald test p-value: treatment80.genotypeknockout line 
## DataFrame with 27710 rows and 6 columns
##               baseMean log2FoldChange     lfcSE      stat    pvalue      padj
##              <numeric>      <numeric> <numeric> <numeric> <numeric> <numeric>
## AT1G01010.1 481.647863     -0.0360314  0.340649 -0.105773  0.915763  0.978489
## AT1G01020.1   0.699483     -0.6273926  4.139936 -0.151546  0.879545        NA
## AT1G01020.2   9.829772     -0.3999154  0.965505 -0.414204  0.678725        NA
## AT1G01030.1  90.232278      0.1472727  0.277684  0.530360  0.595862  0.871599
## AT1G01040.1 153.217100     -0.3981934  0.282761 -1.408232  0.159063  0.519782
## ...                ...            ...       ...       ...       ...       ...
## ATMG01370.1   0.800246        4.29449   3.98368  1.078021 0.2810245        NA
## ATMG01380.1   0.343591        1.87808   9.36998  0.200435 0.8411400        NA
## ATMG01390.1   3.526977        1.61474   2.06880  0.780519 0.4350853        NA
## ATMG01400.1   5.379710        1.87345   1.80558  1.037589 0.2994614        NA
## ATMG01410.1  11.977327       -2.23519   1.24253 -1.798904 0.0720339  0.354658

Normalization

1/28/25

The following code chunk is the normalization of the data’s read counts for future use:

# normalization of read counts for future use 
dds <- estimateSizeFactors(dds)
sizeFactors(dds)
## Col01_1_1 Col01_1_2 Col01_1_3 Col01_2_1 Col01_2_2 Col01_2_3 Col01_3_1 Col01_3_2 
## 1.0592495 0.9060561 0.7694873 0.7654071 0.8409846 0.7989893 0.9593589 1.0299932 
## Col01_3_3 Col01_4_1 Col01_4_2 Col01_4_3 Col01_5_1 Col01_5_2 Col01_5_3  ST35_1_1 
## 1.0102252 0.8769932 1.2538535 1.2085315 0.8529351 1.0513209 1.1874095 1.0163931 
##  ST35_1_2  ST35_1_3  ST35_2_1  ST35_2_2  ST35_2_3  ST35_3_1  ST35_3_2  ST35_3_3 
## 0.8397445 0.8019333 1.0863337 0.9429100 0.8752783 1.0910270 1.2287452 1.3388948 
##  ST35_4_1  ST35_4_2  ST35_4_3  ST35_5_1  ST35_5_2  ST35_5_3 
## 0.9652094 1.0114658 1.1666743 1.1550933 1.3651549 1.2601360
normalized_counts <- counts(dds, normalized = TRUE)

# make a box plot using normalized counts 
boxplot(log10(normalized_counts))

# write data to a csv file 
write.csv(normalized_counts, file = "../data/st35_normalized_counts.csv")

5) Explore DESeq2 Results

1/21/25

Let’s begin exploring the results of the differential gene expression analysis by summarizing the results.

  • we will use both the default setting for p-value and our own adjusted p-value of 0.05
# summary results of dds (with a re-leveling of wild_type control vs. each treatment, and interaction between treatment and genotype) 
summary(res_dds)
## 
## out of 27710 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0 (up)       : 892, 3.2%
## LFC < 0 (down)     : 716, 2.6%
## outliers [1]       : 10, 0.036%
## low counts [2]     : 6982, 25%
## (mean count < 12)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
# with adjusted p-value of 0.05
res_0.05_dds <- results(dds, alpha = 0.05)
summary(res_0.05_dds)
## 
## out of 27710 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 589, 2.1%
## LFC < 0 (down)     : 419, 1.5%
## outliers [1]       : 10, 0.036%
## low counts [2]     : 7520, 27%
## (mean count < 14)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

We can also use DESeq2’s contrast function to compare specific factor levels after re-leveling. This allows us to make all the necessary comparisons we need without having to re-run the DESeq function with different parameters every time.

Below are the comparison results between both the ____ and the ____:

The struggle here is that I need to be able to be more exact with my comparisons; how can I compare wild_type_0 to knockout_line_0 when the meta-data is contained in different columns (something the contrast function does not accommodate for).

  • The answer is that I would need to make a combined and more specific column in colData
#USEFUL FOR REFERENCE ON USING BASE CONTRAST() FUNCTION BUT THIS CODE CHUNK DOES NOT RUN

# contrast different factor levels (i.e. each treatment vs. ST-35, Col-0 vs. ST-35, etc.)
res_0_80 <- results(dds, contrast = c("treatment", "0", "80"), alpha = 0.05) # p-value of 0.05; this alpha = 0.05 does not work 
# do shrinkage after each individual contrast 

# convert DESeq2 results to a data frame
res_0_80_df <- as.data.frame(res_0_80)

# add the gene IDs as a column in the data frame
res_0_80_df$GeneID <- rownames(res_0_80_df)

# reorder columns to place GeneID as the first column
res_0_80_df <- res_0_80_df[, c("GeneID", colnames(res_0_80_df)[-ncol(res_0_80_df)])]

# write output to a csv file 
write.csv(res_0_80_df, "../data/DESeq2_results_0_vs_80.csv", row.names = FALSE) # filter and save only those values have are < 0.05 (get creative with padj in the res_0_80_df)

6) Visualizing the Results

The following are different ways we can visualize the results of our differential gene expression analysis.

PCA (Principal Component Analysis)

1/21/25

Extract and Transform Normalized Data

Make sure to use the the normalized data counts and not the DESeq result output!

# perform variance stabilizing transformation (VST)
vsd <- vst(dds, blind = FALSE)

# Alternatively, use rlog (log transformation)
# vsd <- rlog(dds, blind = FALSE)

Perform PCA

# extract the transformed data
transformed_counts <- assay(vsd)

# perform PCA on the transformed counts
pca <- prcomp(t(transformed_counts))

Extract PCA Results for Plotting

# create a data frame with PCA results and sample metadata
pca_dat <- as.data.frame(pca$x)  # PCA scores for each sample
pca_dat$sample <- rownames(pca_dat)  # sample names
pca_dat$treatment <- colData(dds)$treatment  # grouping variable (treatment condition)
pca_dat$genotype <- colData(dds)$genotype  # add genotype to the PCA data

Create PCA Plot

# plot the first two principal components
ggplot(pca_dat, aes(x = PC1, y = PC2, color = treatment, shape = genotype, label = sample)) +
  geom_point(size = 3) +
  geom_text(vjust = -1, hjust = 0.5, size = 3) +
  labs(
    title = "PCA Plot of Arabidopsis Selenium Treatment:\nSulfur Transport Knockout-Line and Wild-Type Samples",
    x = paste0("PC1: ", round(summary(pca)$importance[2, 1] * 100, 1), "% Variance"),
    y = paste0("PC2: ", round(summary(pca)$importance[2, 2] * 100, 1), "% Variance"),
    color = "Treatment",
    shape = "Genotype"
  ) +
  theme_minimal()

MA Plot

1/28/25

Genotype Shrinkage

# Step 1: Compute results for the contrast
res <- results(dds, contrast = c("genotype", "knockout_line", "wild_type"))

# Step 2: Apply shrinkage to the results
res_shrink <- lfcShrink(dds, coef = "genotype_knockout_line_vs_wild_type", type = "apeglm")

# check results
summary(res_shrink)
## 
## out of 27710 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0 (up)       : 1513, 5.5%
## LFC < 0 (down)     : 1902, 6.9%
## outliers [1]       : 10, 0.036%
## low counts [2]     : 6445, 23%
## (mean count < 10)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

Update the PCA Data Frame

# create a data frame with the results from lfcShrink
pca_dat_shrink <- as.data.frame(res_shrink)
pca_dat_shrink$significance <- ifelse(pca_dat_shrink$padj < 0.05, "Significant", "Not Significant")

Create MA Plot

# create MA plot using ggplot2
ggplot(pca_dat_shrink, aes(x = baseMean, y = log2FoldChange, color = significance)) +
  geom_point(size = 1) +
  scale_color_manual(values = c("Significant" = "blue", "Not Significant" = "black")) +
  scale_x_log10() +  # Apply log scale to x-axis (baseMean)
  labs(title = "MA Plot: Differential Expression with Shrinkage",
       x = "Mean of Normalized Counts (log scale)", 
       y = "Shrunken Log2 Fold Change") +
  theme_minimal()

7) Analyses for All Treatment Combinations

2/3/25 - 2/4/25

Subsetting ‘colData’ and ‘data_counts’ into Individual Data Frames

I will be subsetting our properly structured ‘colData’ data frame produced earlier in this script (found under “1. Preparing Count Data”) as well as the ‘data_counts’ data frame to match column and rownames. This will allow us to properly utilize the contrast() function DESeq2 offers, giving us the ability to more dynamically compare our samples by treating the varying columns as independent factors.

As a reminder, we will be making many comparisons, of which are as follows:

  • knockout_line treatment vs. wild_type treatment
    • KO_20 vs. WT_20, KO_40 vs. WT_40, etc.
  • wild_type control vs. knockout_line control
    • WT_0 vs. KO_0
  • wild_type treatment vs. wild_type control
    • WT_20 vs. WT_0, WT_40 vs. WT_0, etc.
  • knockout_line treatment vs. knockout_line control
    • KO_20 vs. KO_0, KO_40 vs. KO_0, etc.

We will be using a total of 4 colData and data_counts variants (including the original colData and data_counts created earlier in this script) that will utilize either the ‘treatment’ or ‘group’ column as their contrast arguments depending on the type of specificity our colData allows.

Original colData and data_counts (for KO Treatment vs. WT Treatment)

colData:
treatment genotype group
Col01_1_1 0 wild_type wt_0
Col01_1_2 0 wild_type wt_0
Col01_1_3 0 wild_type wt_0
Col01_2_1 20 wild_type wt_20
Col01_2_2 20 wild_type wt_20
Col01_2_3 20 wild_type wt_20
data_counts:
Col01_1_1 Col01_1_2 Col01_1_3 Col01_2_1 Col01_2_2 Col01_2_3 Col01_3_1 Col01_3_2 Col01_3_3 Col01_4_1 Col01_4_2 Col01_4_3 Col01_5_1 Col01_5_2 Col01_5_3 ST35_1_1 ST35_1_2 ST35_1_3 ST35_2_1 ST35_2_2 ST35_2_3 ST35_3_1 ST35_3_2 ST35_3_3 ST35_4_1 ST35_4_2 ST35_4_3 ST35_5_1 ST35_5_2 ST35_5_3
AT1G01010.1 179 242 121 160 247 188 398 601 373 568 820 855 586 800 892 334 152 90 350 396 288 576 577 786 613 712 777 927 936 972
AT1G01020.1 0 0 0 0 0 0 1 0 1 1 1 0 2 1 4 0 0 0 0 0 0 1 0 0 2 3 0 0 6 0
AT1G01020.2 15 8 4 23 3 10 4 2 7 8 13 10 9 5 19 19 5 5 6 6 6 23 12 15 13 13 5 10 12 11
AT1G01030.1 131 100 111 72 78 72 94 88 96 80 93 96 52 85 113 119 81 93 75 78 77 93 116 119 72 80 80 70 100 123
AT1G01040.1 148 90 99 91 128 85 149 147 156 189 212 188 154 214 178 178 121 125 109 84 142 176 156 189 186 160 227 207 243 209
AT1G01040.2 5 5 7 8 8 15 9 17 13 23 20 26 16 20 14 16 11 6 19 10 21 12 10 23 31 27 35 34 24 13

Display does not obviously show that all rows/columns are included.

Only Controls

colData_controls:
treatment genotype group
Col01_1_1 0 wild_type wt_0
Col01_1_2 0 wild_type wt_0
Col01_1_3 0 wild_type wt_0
ST35_1_1 0 knockout_line ko_0
ST35_1_2 0 knockout_line ko_0
ST35_1_3 0 knockout_line ko_0
data_counts_controls:
Col01_1_1 Col01_1_2 Col01_1_3 ST35_1_1 ST35_1_2 ST35_1_3
AT1G01010.1 179 242 121 334 152 90
AT1G01020.1 0 0 0 0 0 0
AT1G01020.2 15 8 4 19 5 5
AT1G01030.1 131 100 111 119 81 93
AT1G01040.1 148 90 99 178 121 125
AT1G01040.2 5 5 7 16 11 6

Only Wild-Type Samples

colData_wt:
treatment genotype group
Col01_1_1 0 wild_type wt_0
Col01_1_2 0 wild_type wt_0
Col01_1_3 0 wild_type wt_0
Col01_2_1 20 wild_type wt_20
Col01_2_2 20 wild_type wt_20
Col01_2_3 20 wild_type wt_20
data_counts_wt:
Col01_1_1 Col01_1_2 Col01_1_3 Col01_2_1 Col01_2_2 Col01_2_3 Col01_3_1 Col01_3_2 Col01_3_3 Col01_4_1 Col01_4_2 Col01_4_3 Col01_5_1 Col01_5_2 Col01_5_3
AT1G01010.1 179 242 121 160 247 188 398 601 373 568 820 855 586 800 892
AT1G01020.1 0 0 0 0 0 0 1 0 1 1 1 0 2 1 4
AT1G01020.2 15 8 4 23 3 10 4 2 7 8 13 10 9 5 19
AT1G01030.1 131 100 111 72 78 72 94 88 96 80 93 96 52 85 113
AT1G01040.1 148 90 99 91 128 85 149 147 156 189 212 188 154 214 178
AT1G01040.2 5 5 7 8 8 15 9 17 13 23 20 26 16 20 14

Only Knockout-Line Samples

colData_ko:
treatment genotype group
ST35_1_1 0 knockout_line ko_0
ST35_1_2 0 knockout_line ko_0
ST35_1_3 0 knockout_line ko_0
ST35_2_1 20 knockout_line ko_20
ST35_2_2 20 knockout_line ko_20
ST35_2_3 20 knockout_line ko_20
data_counts_ko:
ST35_1_1 ST35_1_2 ST35_1_3 ST35_2_1 ST35_2_2 ST35_2_3 ST35_3_1 ST35_3_2 ST35_3_3 ST35_4_1 ST35_4_2 ST35_4_3 ST35_5_1 ST35_5_2 ST35_5_3
AT1G01010.1 334 152 90 350 396 288 576 577 786 613 712 777 927 936 972
AT1G01020.1 0 0 0 0 0 0 1 0 0 2 3 0 0 6 0
AT1G01020.2 19 5 5 6 6 6 23 12 15 13 13 5 10 12 11
AT1G01030.1 119 81 93 75 78 77 93 116 119 72 80 80 70 100 123
AT1G01040.1 178 121 125 109 84 142 176 156 189 186 160 227 207 243 209
AT1G01040.2 16 11 6 19 10 21 12 10 23 31 27 35 34 24 13

Function for Creating Varying ‘dds’ Objects

I’ve written a function that allows for easy manipulation and production of ‘dds’ objects using the DESeqDataSetFromMatrix() function. It seems very similar to simply using the DESeq2 function itself, however, this allows for cleaner production of the dds objects and includes chosen filtering of gene counts. It also includes error messages to gracefully halt improper inputs.

  • read_counts = the countData input (assumed it is properly structured)
  • column_data = the colData meta data that also contains the columns used for the design of the dds object
  • design_parameter = your chosen design formula that aligns with the colData column names
  • filter_low_counts = a chosen limit to filter out gene counts; default is set to no filtering if not explicitly stated

This function only works if the DESeq2 package is installed and running on the same script.

dds_generator <- function(read_counts, column_data, design_parameter, filter_low_counts = NULL) {
  if (!is.character(design_parameter)) {
    stop("'design_parameter' must be a character string representing the design formula.")
  }
  
  # validate design variables exist in column_data
  design_vars <- unlist(strsplit(design_parameter, " \\+ | \\* "))
  missing_vars <- setdiff(design_vars, colnames(column_data))
  if (length(missing_vars) > 0) {
    stop("The following design variables are missing in 'column_data': ", paste(missing_vars, collapse = ", "))
  }
  
  # convert design parameter input into formula
  design_formula <- as.formula(paste("~", design_parameter))
  
  # creating DESeq2 object
  dds <- DESeqDataSetFromMatrix(countData = read_counts,
                                colData = column_data,
                                design = design_formula)
  
  # remove low gene counts only if a filtering threshold is specified
  if (!is.null(filter_low_counts) && is.numeric(filter_low_counts) && filter_low_counts > 0) {
    keep_counts <- rowSums(counts(dds)) >= filter_low_counts
    dds <- dds[keep_counts,]
  }
  
  return(dds)
}

Generating and Re-leveling ‘dds’ Objects

Using all of the particular colData sub-sets, I will be producing varying ‘dds’ objects that will have different design formulas depending on how specific we need the comparisons between samples to be. These objects will then be implemented with the contrast function for more specific comparisons within each subset.

  • Each ‘dds’ object will be produced and filtered using the dds_generator() function

All ‘dds’ objects will be factor re-leveled to reference the baseline which is the wild-type control sample (treatment = 0 and genotype = wild_type or group = wild_type_0). This will be the same for every analyses.

KO Treatment vs. WT Treatment

dds_treatment <- dds_generator(data_counts, colData, "group", 10)

# set reference level: group to wt_0
dds_treatment$group <- relevel(dds_treatment$group, ref = "wt_0")

dds_treatment
## class: DESeqDataSet 
## dim: 27710 30 
## metadata(1): version
## assays(1): counts
## rownames(27710): AT1G01010.1 AT1G01020.1 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(30): Col01_1_1 Col01_1_2 ... ST35_5_2 ST35_5_3
## colData names(3): treatment genotype group

Only Controls

dds_controls <- dds_generator(data_counts_controls, colData_controls, "genotype", 10)

# set reference level: genotype to wild_type 
dds_controls$genotype <- relevel(dds_controls$genotype, ref = "wild_type")

dds_controls
## class: DESeqDataSet 
## dim: 23973 6 
## metadata(1): version
## assays(1): counts
## rownames(23973): AT1G01010.1 AT1G01020.2 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(6): Col01_1_1 Col01_1_2 ... ST35_1_2 ST35_1_3
## colData names(3): treatment genotype group

Only Wild-Type Samples

dds_wt <- dds_generator(data_counts_wt, colData_wt, "treatment", 10)

# set reference level: treatment to 0
dds_wt$treatment <- relevel(dds_wt$treatment, ref = "0")

dds_wt
## class: DESeqDataSet 
## dim: 26602 15 
## metadata(1): version
## assays(1): counts
## rownames(26602): AT1G01010.1 AT1G01020.1 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(15): Col01_1_1 Col01_1_2 ... Col01_5_2 Col01_5_3
## colData names(3): treatment genotype group

Only Knockout-Line Samples

dds_ko <- dds_generator(data_counts_ko, colData_ko, "treatment", 10)

# set reference level: treatment to 0
dds_ko$treatment <- relevel(dds_ko$treatment, ref = "0")

dds_ko
## class: DESeqDataSet 
## dim: 26818 15 
## metadata(1): version
## assays(1): counts
## rownames(26818): AT1G01010.1 AT1G01020.1 ... ATMG01400.1 ATMG01410.1
## rowData names(0):
## colnames(15): ST35_1_1 ST35_1_2 ... ST35_5_2 ST35_5_3
## colData names(3): treatment genotype group

Functions for Writing Shrunken Comparison Data to CSV’s

To allow for easier reproducibility and less redundancy in the code, I’ve written two functions that allow for producing dynamic contrasts that utilize either apeglm shrinkage with coefficients or ashr shrinkage with contrasts, and writes the output to a dynamically named CSV file.

coef_shrinkage_contrast() Function

As mentioned, the coef_shrinkage_contrast() function using apeglm shrinkage and coefficients from the ‘dds’ levels. This is especially useful to maintain indepdence amongst comparisons and the factor levels.

  • dds_object = the specific ‘dds’ object used for the contrast
  • column = the specific column used to reference the factor levels being contrasted
  • level_1 = the first factor level being compared
  • priority_level_2 = the second factor level being compared (generally the preferred baseline according to reference level)
  • output_dir = the relative path to which you want your dynamically named CSV file to go (the default is set for my personal computer; adjust it to your needs)

This function also dynamically incorporates the proper coefficient using the column, level_1, and priority_level_2 parameters. It also has a built-in check to make sure the coefficient exists in said dds object.

This function requires the apeglm package installed and running on the same script to work.

coef_shrinkage_contrast <- function(dds_object, column, level_1, priority_level_2, output_dir = "../data/arabidopsis_deseq2_results/") {
  # construct the expected coefficient name
  coef_name <- paste0(column, "_", level_1, "_vs_", priority_level_2)
  
  # ensure the coefficient exists
  dds_name <- deparse(substitute(dds_object))  # grab the object's name as a string 
  if (!(coef_name %in% resultsNames(dds_object))) {
    stop(paste("Coefficient", coef_name, "not found in object", dds_name, ". Check resultsNames(", dds_name, ")."))
    }

  # apply shrinkage using apeglm
  shrink_result <- lfcShrink(dds_object, coef = coef_name, type = "apeglm")

  # convert the shrinkage results to a data frame
  result_df <- as.data.frame(shrink_result)

  # add the gene IDs as a column in the data frame
  result_df$GeneID <- rownames(result_df)

  # re-order columns to place GeneID as the first column
  result_df <- result_df[, c("GeneID", colnames(result_df)[-ncol(result_df)])]

  # get the name of the dds_object (used for output_file name)
  dds_name <- deparse(substitute(dds_object))

  # generate dynamic output file name
  output_file <- paste0(output_dir, "deseq2_", dds_name, "_", level_1, "_vs_", priority_level_2, ".csv")

  # write output to a CSV file
  write.csv(result_df, output_file, row.names = FALSE)
}

ashr_shrinkage_contrast() Function

The following ashr_shrinkage_contrast() function utilizes the ashr shrinkage estimator along with the contrast function to allow mutability in contrasting factor levels that are were not originally set during factor re-leveling. In other words, we can compare different factor levels that are do not involve the reference level.

All of the parameters used in the function above are used and represented the same in this one. The same checks are also in place.

This function requires the ashr package to be installed and running on the same script.

ashr_shrinkage_contrast <- function(dds_object, column, level_1, priority_level_2, output_dir = "../data/arabidopsis_deseq2_results/") {
  # ensure the contrast levels exist
  dds_name <- deparse(substitute(dds_object))  # grab the object's name as a string 
  factor_levels <- levels(colData(dds_object)[[column]])  # Get levels of the specified column
  
  # check whether the levels exist in the factor column
  if (!(level_1 %in% factor_levels) | !(priority_level_2 %in% factor_levels)) {
    stop(paste("Levels", level_1, "or", priority_level_2, "not found in column", column, "of object", dds_name, ". Check levels(colData(",
               dds_name, ")[[", column, "]])."))
    }
  
  # apply shrinkage using ashr and contrast
  shrink_result <- lfcShrink(dds_object, contrast = c(column, level_1, priority_level_2), type = "ashr")

  # convert the shrinkage results to a data frame
  result_df <- as.data.frame(shrink_result)

  # add the gene IDs as a column in the data frame
  result_df$GeneID <- rownames(result_df)

  # re-order columns to place GeneID as the first column
  result_df <- result_df[, c("GeneID", colnames(result_df)[-ncol(result_df)])]

  # generate dynamic output file name
  output_file <- paste0(output_dir, "deseq2_", dds_name, "_", level_1, "_vs_", priority_level_2, ".csv")

  # write output to a CSV file
  write.csv(result_df, output_file, row.names = FALSE)
}

Run Varying DESeq2 Analyses and Contrasts

Below will contain the code running all of the various DESeq2 analyses we need along with specific contrasts. Each contrast will be written to a CSV file that will be used for further analyses. There will be many files saved to a subdirectory within my ‘data’ directory.

KO Treatment vs. WT Treatment

# run the differential gene expression analysis
dds_treatment <- DESeq(dds_treatment)

# view what conditions were contrasted to make sure they match what we actually meant to compare 
resultsNames(dds_treatment)
##  [1] "Intercept"           "group_ko_0_vs_wt_0"  "group_ko_20_vs_wt_0"
##  [4] "group_ko_40_vs_wt_0" "group_ko_60_vs_wt_0" "group_ko_80_vs_wt_0"
##  [7] "group_wt_20_vs_wt_0" "group_wt_40_vs_wt_0" "group_wt_60_vs_wt_0"
## [10] "group_wt_80_vs_wt_0"
# store the results in an object 
res_dds_treatment <- results(dds_treatment)

# view DESeq2 results 
res_dds_treatment
## log2 fold change (MLE): group wt 80 vs wt 0 
## Wald test p-value: group wt 80 vs wt 0 
## DataFrame with 27710 rows and 6 columns
##               baseMean log2FoldChange     lfcSE      stat      pvalue
##              <numeric>      <numeric> <numeric> <numeric>   <numeric>
## AT1G01010.1 481.647863       1.889492  0.241198  7.833777 4.73430e-15
## AT1G01020.1   0.699483       3.450224  2.921736  1.180881 2.37650e-01
## AT1G01020.2   9.829772       0.135655  0.685272  0.197958 8.43078e-01
## AT1G01030.1  90.232278      -0.655917  0.196783 -3.333195 8.58548e-04
## AT1G01040.1 153.217100       0.534500  0.202396  2.640861 8.26957e-03
## ...                ...            ...       ...       ...         ...
## ATMG01370.1   0.800246      -1.137773  2.912105 -0.390705   0.6960154
## ATMG01380.1   0.343591       0.785622  6.662562  0.117916   0.9061343
## ATMG01390.1   3.526977       2.062884  1.246262  1.655257   0.0978723
## ATMG01400.1   5.379710       1.215300  1.086761  1.118277   0.2634489
## ATMG01410.1  11.977327       1.988103  0.845472  2.351472   0.0186993
##                    padj
##               <numeric>
## AT1G01010.1 6.72910e-14
## AT1G01020.1 3.62134e-01
## AT1G01020.2 9.05699e-01
## AT1G01030.1 2.75096e-03
## AT1G01040.1 2.09610e-02
## ...                 ...
## ATMG01370.1   0.7999480
## ATMG01380.1          NA
## ATMG01390.1   0.1764977
## ATMG01400.1   0.3929179
## ATMG01410.1   0.0427462
# summary results of res_dds_treatment (with re-level of group = wt_0)
summary(res_dds_treatment, 0.05)
## 
## out of 27710 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 6533, 24%
## LFC < 0 (down)     : 5605, 20%
## outliers [1]       : 10, 0.036%
## low counts [2]     : 538, 1.9%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

We will be using the ashr_shrinkage_contrast() function for the comparisons regarding dds_treatment as they have a reference level of wt_0 but require contrasts between treatments of both genotypes to each other, not to the controls. In other words, we will be comparing wt_20 vs. ko_20, wt_40 vs. ko_40, etc., in these ‘dds’ results. Therefore, we need the ability to be specific about our contrasts (using the contrast() function) and still be able to shrink the data which is not functional with apeglm, but is functional with ashr.

WT_20 vs. KO_20

ashr_shrinkage_contrast(dds_treatment, "group", "ko_20", "wt_20")

WT_40 vs. KO_40

ashr_shrinkage_contrast(dds_treatment, "group", "ko_40", "wt_40")

WT_60 vs. KO_60

ashr_shrinkage_contrast(dds_treatment, "group", "ko_60", "wt_60")

WT_80 vs. KO_80

ashr_shrinkage_contrast(dds_treatment, "group", "ko_80", "wt_80")

Only Controls

# run the differential gene expression analysis
dds_controls <- DESeq(dds_controls)

# view what conditions were contrasted to make sure they match what we actually meant to compare 
resultsNames(dds_controls)
## [1] "Intercept"                           "genotype_knockout_line_vs_wild_type"
# store the results in an object 
res_dds_controls <- results(dds_controls)

# view DESeq2 results 
res_dds_controls
## log2 fold change (MLE): genotype knockout line vs wild type 
## Wald test p-value: genotype knockout line vs wild type 
## DataFrame with 23973 rows and 6 columns
##              baseMean log2FoldChange     lfcSE       stat    pvalue      padj
##             <numeric>      <numeric> <numeric>  <numeric> <numeric> <numeric>
## AT1G01010.1 180.09710      0.0909285  0.367753   0.247254  0.804712  0.959745
## AT1G01020.2   8.75265      0.1527107  0.890280   0.171531  0.863806        NA
## AT1G01030.1 104.59159     -0.1776354  0.263761  -0.673471  0.500647  0.848081
## AT1G01040.1 124.72601      0.3887219  0.255211   1.523142  0.127723  0.508203
## AT1G01040.2   8.25645      0.9542243  0.853555   1.117941  0.263592        NA
## ...               ...            ...       ...        ...       ...       ...
## ATMG01320.1  33.85034      0.1306066  0.462083  0.2826478  0.777447  0.953273
## ATMG01340.1   1.67730      1.1252154  1.893954  0.5941091  0.552439        NA
## ATMG01350.1  60.47710      0.0228543  0.398252  0.0573866  0.954237  0.991725
## ATMG01400.1   1.94285     -2.4218435  2.215832 -1.0929727  0.274406        NA
## ATMG01410.1   4.86897     -0.1278612  1.396298 -0.0915716  0.927038        NA
# summary results of res_dds_controls (with re-level of genotype = wild_type)
summary(res_dds_controls, 0.05)
## 
## out of 23973 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 486, 2%
## LFC < 0 (down)     : 587, 2.4%
## outliers [1]       : 28, 0.12%
## low counts [2]     : 4183, 17%
## (mean count < 11)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

For the all of the remaining comparisons, we will be using the coef_shrinkage_contrast() function as the data for the dds_controls, dds_wt, and dds_ko objects have all been subset and re-leveled to reference the wild_type control variant where possible (dds_controls and dds_wt) or the knockout_line control variant in dds_ko. In these cases, we are comparing all instances specifically to the reference level, therefore, the intercepts are viable coefficients to use with the apeglm shrinkage estimator. Although we cannot use the contrast() function with apeglm.

The vignette’s on DESeq2 describe apeglm as a higher performance estimator than ashr. For now, I am using a mix for the sake of ease in repeated production of the resulting dds data sets. Depending on our future needs, we can apply either function to all the data to maintain consistency.

WT_0 vs. KO_0

I honestly should not need to even contrast this as the only rows in the ‘dds_controls’ are the triplactes of the control groups for both the knockout_line and the wild_type variants. However, I have the run_shrinkage_contrast() function already and it seems to work pretty well, and incorporates shrinkage so we will just use it.

Will contrasting have any negative effect on the outcome of an already completed comparison?

coef_shrinkage_contrast(dds_controls, "genotype", "knockout_line", "wild_type")

Only Wild-Type Samples

# run the differential gene expression analysis
dds_wt <- DESeq(dds_wt)

# view what conditions were contrasted to make sure they match what we actually meant to compare 
resultsNames(dds_wt)
## [1] "Intercept"         "treatment_20_vs_0" "treatment_40_vs_0"
## [4] "treatment_60_vs_0" "treatment_80_vs_0"
# store the results in an object 
res_dds_wt <- results(dds_wt)

# view DESeq2 results 
res_dds_wt
## log2 fold change (MLE): treatment 80 vs 0 
## Wald test p-value: treatment 80 vs 0 
## DataFrame with 26602 rows and 6 columns
##               baseMean log2FoldChange     lfcSE      stat      pvalue
##              <numeric>      <numeric> <numeric> <numeric>   <numeric>
## AT1G01010.1 437.237306       1.879602  0.204738  9.180516 4.29029e-20
## AT1G01020.1   0.672511       3.442177  2.433941  1.414240 1.57291e-01
## AT1G01020.2   9.278346       0.130042  0.762051  0.170648 8.64501e-01
## AT1G01030.1  89.749801      -0.667420  0.209511 -3.185610 1.44449e-03
## AT1G01040.1 143.898223       0.524872  0.204778  2.563133 1.03732e-02
## ...                ...            ...       ...       ...         ...
## ATMG01350.1   81.75085       0.586523  0.318424   1.84196  0.06548108
## ATMG01360.1    5.36252       3.426499  1.239572   2.76426  0.00570523
## ATMG01390.1    4.51224       2.052131  1.233042   1.66428  0.09605587
## ATMG01400.1    6.88725       1.190211  0.895700   1.32880  0.18391237
## ATMG01410.1   15.96561       1.988374  0.772065   2.57540  0.01001252
##                    padj
##               <numeric>
## AT1G01010.1 7.12325e-19
## AT1G01020.1          NA
## AT1G01020.2 9.11608e-01
## AT1G01030.1 4.10853e-03
## AT1G01040.1 2.42576e-02
## ...                 ...
## ATMG01350.1   0.1205520
## ATMG01360.1   0.0142260
## ATMG01390.1   0.1669259
## ATMG01400.1   0.2840578
## ATMG01410.1   0.0235037
# summary results of res_dds_wt (with re-level of treatment = 0 for wild_type)
summary(res_dds_wt, 0.05)
## 
## out of 26602 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 6515, 24%
## LFC < 0 (down)     : 5839, 22%
## outliers [1]       : 19, 0.071%
## low counts [2]     : 516, 1.9%
## (mean count < 1)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

WT_0 vs. WT_20

coef_shrinkage_contrast(dds_wt, "treatment", "20", "0")

WT_0 vs. WT_40

coef_shrinkage_contrast(dds_wt, "treatment", "40", "0")

WT_0 vs. WT_60

coef_shrinkage_contrast(dds_wt, "treatment", "60", "0")

WT_0 vs. WT_80

coef_shrinkage_contrast(dds_wt, "treatment", "80", "0")

Only Knockout-Line Samples

# run the differential gene expression analysis
dds_ko <- DESeq(dds_ko)

# view what conditions were contrasted to make sure they match what we actually meant to compare 
resultsNames(dds_ko)
## [1] "Intercept"         "treatment_20_vs_0" "treatment_40_vs_0"
## [4] "treatment_60_vs_0" "treatment_80_vs_0"
# store the results in an object 
res_dds_ko <- results(dds_ko)

# view DESeq2 results 
res_dds_ko
## log2 fold change (MLE): treatment 80 vs 0 
## Wald test p-value: treatment 80 vs 0 
## DataFrame with 26818 rows and 6 columns
##               baseMean log2FoldChange     lfcSE      stat      pvalue
##              <numeric>      <numeric> <numeric> <numeric>   <numeric>
## AT1G01010.1 528.128492       1.848828  0.257871  7.169578 7.52291e-13
## AT1G01020.1   0.724119       2.813109  3.637539  0.773355 4.39312e-01
## AT1G01020.2  10.397760      -0.272373  0.651345 -0.418170 6.75823e-01
## AT1G01030.1  90.532212      -0.515260  0.219451 -2.347949 1.88771e-02
## AT1G01040.1 162.893461       0.130488  0.209979  0.621433 5.34315e-01
## ...                ...            ...       ...       ...         ...
## ATMG01360.1   2.663776        1.83537  1.614998  1.136453   0.2557669
## ATMG01370.1   0.972828        3.15069  2.232032  1.411577   0.1580745
## ATMG01390.1   2.418109        3.67246  1.743147  2.106800   0.0351349
## ATMG01400.1   3.689406        3.10262  1.623886  1.910617   0.0560538
## ATMG01410.1   7.511026       -0.25594  0.979801 -0.261216   0.7939258
##                    padj
##               <numeric>
## AT1G01010.1 9.38179e-12
## AT1G01020.1          NA
## AT1G01020.2 7.72073e-01
## AT1G01030.1 4.32020e-02
## AT1G01040.1 6.52509e-01
## ...                 ...
## ATMG01360.1   0.3750086
## ATMG01370.1          NA
## ATMG01390.1   0.0735021
## ATMG01400.1   0.1092704
## ATMG01410.1   0.8614655
# summary results of res_dds_ko (with re-level of treatment = 0 for knockout_line)
summary(res_dds_ko, 0.05)
## 
## out of 26818 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 6266, 23%
## LFC < 0 (down)     : 5259, 20%
## outliers [1]       : 13, 0.048%
## low counts [2]     : 1040, 3.9%
## (mean count < 1)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results

KO_0 vs. KO_20

coef_shrinkage_contrast(dds_ko, "treatment", "20", "0")

KO_0 vs. KO_40

coef_shrinkage_contrast(dds_ko, "treatment", "40", "0")

KO_0 vs. KO_60

coef_shrinkage_contrast(dds_ko, "treatment", "60", "0")

KO_0 vs. KO_80

coef_shrinkage_contrast(dds_ko, "treatment", "80", "0")

CSV Naming Key

2/5/25

The following are defined terms and acronyms that can be used to distinguish the CSV files produced via the ashr_shrinkage_contrast() and coef_shrinkage_contrast() functions.

  • All file titles will consistently include ‘deseq2_1_2_vs_3.csv’:
    • ‘deseq2’ is included as that is the package we are using to conduct these analyses and to produce the object used in the analyses.
    • ‘vs’ simply means versus as this denotes that particular comparison used to produce the data in said file.
    • ‘.csv’ will be consistent as we are writing to a csv file; can be changed within the function to whatever file format preferred.
    • Functions utilizes snake_case format by placing an underscore between all terms in the title. It will only be a mix of snake_case and other formatting if you do not use snake_case to name row values in your colData data frame.
    • Each number represents a part of your contrast parameters used in either function (i.e the numbers will not actually be in your saved file name):
      • 1 = the name of your used ‘dds’ object
      • 2 = the first factor level used in your contrast
      • 3 = the second factor level used in your contrast

Key Title Terms:

  • deseq2 = package used to run analysis

  • ‘dds’ Object Names:

    • dds_controls = the ‘dds’ object used for analysis only consisted of the control data for both wild_type and knockout_line samples
    • dds_ko = the ‘dds’ object used for analysis only consisted of the knockout_line data
    • dds_wt = the ‘dds’ object used for analysis only consisted of the wild_type data
    • dds_treatment = the ‘dds’ object used for analysis consisted of all samples (including controls)
  • Contrast Parameters:

    • knockout_line = genotype factor level for knockout-line samples
    • wild_type = genotype factor level for wild-type samples
    • 0 = treatment factor level for control samples (no treatment)
    • 20 = treatment factor level for samples treated with 20 uM
    • 40 = treatment factor level for samples treated with 40 uM
    • 60 = treatment factor level for samples treated with 60 uM
    • 80 = treatment factor level for samples treated with 80 uM

For example, one of the csv files produced from the above analyses had the title of: deseq2_dds_ko_20_vs_0.csv

  • The ‘dds’ object used consisted of only the knockout_line samples and is thus named ‘dds_ko’ (ko = knockout).
  • This file contains the contrast results of the knockout_line samples treated with 20 uM against the control sample with 0 uM.

Therefore, this CSV file contains the resulting contrast between the knockout_line sample treated with 20 uM and the knockout_line control sample with 0 uM.

8) Gene Ontology Enrichment Analysis

2/18/25

Be sure to install and run the following libraries for the analysis:

# be sure to run install.packages("BiocManager") and use BiocManager::install("package") order to install and run the following libraries
library(clusterProfiler)
library(AnnotationDbi) # warning: will mask select() from dpylr 
library(org.At.tair.db) # database used for arabidopsis

Read in Individual Treatment Comparison CSV Files

In order to conduct GO enrichment analysis, we will need to read in our produced CSV files (from section #7) as data frames. The following chunk of code will loop through our data folder that contains the CSV files and save them as individual data frames in our global environment for further use.

The code will keep the same name for the files but change the beginning portion of “deseq2_” to “df_” and will only read in those rows with a padj < 0.05. Be sure to also replace the file path when creating the data_folder variable to wherever your CSV files were saved (will be the same path as used in the coef and ashr functions if those were utilized).

# define the data folder path
data_folder <- "../data/arabidopsis_deseq2_results/"  # change to your actual data folder path 

# list all CSV files in the folder
csv_files <- list.files(data_folder, pattern = "\\.csv$", full.names = TRUE)

# avoid scientific notation
options(scipen = 999)

# loop through each file and create a filtered data frame (padj < 0.05)
for (file in csv_files) {
  # extract file name without extension
  df_name <- tools::file_path_sans_ext(basename(file))
  
  # replace "deseq2_" with "df_" in the variable name (should always work if using the ashr and coef functions)
  df_name <- sub("^deseq2_", "df_", df_name)
  
  # read the CSV into a data frame
  df <- read.csv(file, stringsAsFactors = FALSE)
  
  # filter rows where column padj < 0.05 and remove rows where padj = NA 
  df <- df[!is.na(df$padj) & df$padj < 0.05, ]
  
  # save the data frames to the global environment
  assign(df_name, df, envir = .GlobalEnv)
}

GO_results() Function

Function made to allow for modular Gene Ontology Enrichment analysis result output. The function’s parameters are as follows:

  • dataframe = dataframe of Deseq2 results of a specific comparison
  • up_or_down = input “up” to subset up-regulated genes (+log2FoldChange) or “down” to subset down-regulated genes (-log2FoldChange)
  • database = chosen database used for the gene annotation (pulled from BiocManager; default is org.At.tair.db which a database for arabidopsis)
  • key = keyType used for the correct organism and database (default is “TAIR” which is for arabidopsis)
  • ont_category = the major category used to dictate the gene ontology analysis
    • BP = Biological Processes (our default for this function)
    • MF = Molecular Function
    • CC = Cellular Component
GO_results <- function(dataframe, up_or_down, database = "org.At.tair.db", key = "TAIR", ont_category = "BP") {
  # remove any suffix (like ".1") from the GeneIDs (won't work otherwise)
  dataframe$GeneID <- sub("\\..*", "", dataframe$GeneID)
  
  # filter up-regulation or down-regulation based on up_or_down
  if (up_or_down == "up") {
    genes_to_test <- dataframe$GeneID[dataframe$log2FoldChange > 0]
  } else if (up_or_down == "down") {
    genes_to_test <- dataframe$GeneID[dataframe$log2FoldChange < 0]
  } else {
    stop("Invalid value for 'up_or_down'. Use 'up' or 'down'.")
  }
  
  # perform GO enrichment analysis
  go_results <- enrichGO(gene = genes_to_test, OrgDb = database, keyType = key, ont = ont_category)
  
  return(go_results)
  
}

save_GO_results() Function

This function is used to convert the GO analysis results into a more reader-friendly data frame, write the contents to a CSV file, and then sort them into respective folders for either “up” or “down” regulation.

  • go_result = chosen GO results object
  • direction = direction of gene regulation (“up” or “down”) used to determine the folder sorting location
  • file_path = path to parent directory of where your “up” or “down” folders are produced (default is my parent directory)
save_GO_results <- function(go_result, direction, file_path = "../data/arabidopsis_GO_results/") {
  # convert the GO results object to a data frame
  go_result_df <- as.data.frame(go_result)
  
  # get the go_result variable name as a string
  object_name <- deparse(substitute(go_result))
  
  # define the directory based the gene regulation direction ("up" or "down")
  output_folder <- paste0(file_path, direction, "_results/")
  
  # create the folder if it doesn't exist
  dir.create(output_folder, recursive = TRUE, showWarnings = FALSE)
  
  # define the output file name using the go_result variable name (so name it something useful)
  output_file <- paste0(output_folder, object_name, ".csv")
  
  # write the data frame to a CSV file
  write.csv(go_result_df, file = output_file, row.names = FALSE) 
}

Annotating Genes

The following code will utilize clusterProfiler, AnnotationDbi, the GO_results(), and save_GO_results() functions to annotate the genes found in each of the individual CSV files we have just read into data frames. We will select for both up-regulated (+log2FoldChange) and down-regulated (-log2FoldChange) for each CSV file. We will also be conducting this on each of the three gene ontology categories (biological processes, molecular functions, cellular components).

  • We will have double the amount of CSV files split into two different folders.

KO Treatment vs. WT Treatment:

# WT_20 vs. KO_20 
up_BP_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "up") # actual GO results (up-regulated)/default ont_category = "BP"
save_GO_results(up_BP_ko_20_vs_wt_20_GO, "up") # GO results converted to a dataframe and saved to a CSV file 
down_BP_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "down") # (down-regulated)
save_GO_results(down_BP_ko_20_vs_wt_20_GO, "down")

up_MF_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "up", ont_category = "MF")
save_GO_results(up_MF_ko_20_vs_wt_20_GO, "up")
down_MF_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "down", ont_category = "MF")
save_GO_results(down_MF_ko_20_vs_wt_20_GO, "down")

up_CC_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "up", ont_category = "CC")
save_GO_results(up_CC_ko_20_vs_wt_20_GO, "up")
down_CC_ko_20_vs_wt_20_GO <- GO_results(df_dds_treatment_ko_20_vs_wt_20, "down", ont_category = "CC")
save_GO_results(down_CC_ko_20_vs_wt_20_GO, "down")



# WT_40 vs. KO_40
up_BP_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "up")
save_GO_results(up_BP_ko_40_vs_wt_40_GO, "up")
down_BP_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "down")
save_GO_results(down_BP_ko_40_vs_wt_40_GO, "down")

up_MF_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "up", ont_category = "MF")
save_GO_results(up_MF_ko_40_vs_wt_40_GO, "up")
down_MF_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "down", ont_category = "MF")
save_GO_results(down_MF_ko_40_vs_wt_40_GO, "down")

up_CC_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "up", ont_category = "CC")
save_GO_results(up_CC_ko_40_vs_wt_40_GO, "up")
down_CC_ko_40_vs_wt_40_GO <- GO_results(df_dds_treatment_ko_40_vs_wt_40, "down", ont_category = "CC")
save_GO_results(down_CC_ko_40_vs_wt_40_GO, "down")



# WT_60 vs. KO_60 
up_BP_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "up")
save_GO_results(up_BP_ko_60_vs_wt_60_GO, "up")
down_BP_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "down")
save_GO_results(down_BP_ko_60_vs_wt_60_GO, "down")

up_MF_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "up", ont_category = "MF")
save_GO_results(up_MF_ko_60_vs_wt_60_GO, "up")
down_MF_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "down", ont_category = "MF")
save_GO_results(down_MF_ko_60_vs_wt_60_GO, "down")

up_CC_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "up", ont_category = "CC")
save_GO_results(up_CC_ko_60_vs_wt_60_GO, "up")
down_CC_ko_60_vs_wt_60_GO <- GO_results(df_dds_treatment_ko_60_vs_wt_60, "down", ont_category = "CC")
save_GO_results(down_CC_ko_60_vs_wt_60_GO, "down")



# WT_80 vs. KO_80 
up_BP_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "up")
save_GO_results(up_BP_ko_80_vs_wt_80_GO, "up")
down_BP_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "down")
save_GO_results(down_BP_ko_80_vs_wt_80_GO, "down")

up_MF_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "up", ont_category = "MF")
save_GO_results(up_MF_ko_80_vs_wt_80_GO, "up")
down_MF_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "down", ont_category = "MF")
save_GO_results(down_MF_ko_80_vs_wt_80_GO, "down")

up_CC_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "up", ont_category = "CC")
save_GO_results(up_CC_ko_80_vs_wt_80_GO, "up")
down_CC_ko_80_vs_wt_80_GO <- GO_results(df_dds_treatment_ko_80_vs_wt_80, "down", ont_category = "CC")
save_GO_results(down_CC_ko_80_vs_wt_80_GO, "down")

Only Controls:

# WT_0 vs. KO_0
up_BP_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "up")
save_GO_results(up_BP_control_ko_0_vs_wt_0_GO, "up")
down_BP_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "down")
save_GO_results(down_BP_control_ko_0_vs_wt_0_GO, "down")

up_MF_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "up", ont_category = "MF")
save_GO_results(up_MF_control_ko_0_vs_wt_0_GO, "up")
down_MF_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "down", ont_category = "MF")
save_GO_results(down_MF_control_ko_0_vs_wt_0_GO, "down")

up_CC_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "up", ont_category = "CC")
save_GO_results(up_CC_control_ko_0_vs_wt_0_GO, "up")
down_CC_control_ko_0_vs_wt_0_GO <- GO_results(df_dds_controls_knockout_line_vs_wild_type, "down", ont_category = "CC")
save_GO_results(down_CC_control_ko_0_vs_wt_0_GO, "down")

Only Wild-Type Samples:

# WT_0 vs. WT_20 
up_BP_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "up")
save_GO_results(up_BP_wt_20_vs_0_GO, "up")
down_BP_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "down")
save_GO_results(down_BP_wt_20_vs_0_GO, "down")

up_MF_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_wt_20_vs_0_GO, "up")
down_MF_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_wt_20_vs_0_GO, "down")

up_CC_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_wt_20_vs_0_GO, "up")
down_CC_wt_20_vs_0_GO <- GO_results(df_dds_wt_20_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_wt_20_vs_0_GO, "down")



# WT_0 vs. WT_40 
up_BP_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "up")
save_GO_results(up_BP_wt_40_vs_0_GO, "up")
down_BP_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "down")
save_GO_results(down_BP_wt_40_vs_0_GO, "down")

up_MF_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_wt_40_vs_0_GO, "up")
down_MF_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_wt_40_vs_0_GO, "down")

up_CC_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_wt_40_vs_0_GO, "up")
down_CC_wt_40_vs_0_GO <- GO_results(df_dds_wt_40_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_wt_40_vs_0_GO, "down")



# WT_0 vs. WT_60 
up_BP_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "up")
save_GO_results(up_BP_wt_60_vs_0_GO, "up")
down_BP_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "down")
save_GO_results(down_BP_wt_60_vs_0_GO, "down")

up_MF_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_wt_60_vs_0_GO, "up")
down_MF_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_wt_60_vs_0_GO, "down")

up_CC_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_wt_60_vs_0_GO, "up")
down_CC_wt_60_vs_0_GO <- GO_results(df_dds_wt_60_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_wt_60_vs_0_GO, "down")



# WT_0 vs. WT_80 
up_BP_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "up")
save_GO_results(up_BP_wt_80_vs_0_GO, "up")
down_BP_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "down")
save_GO_results(down_BP_wt_80_vs_0_GO, "down")

up_MF_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_wt_80_vs_0_GO, "up")
down_MF_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_wt_80_vs_0_GO, "down")

up_CC_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_wt_80_vs_0_GO, "up")
down_CC_wt_80_vs_0_GO <- GO_results(df_dds_wt_80_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_wt_80_vs_0_GO, "down")

Only Knockout-Line Samples:

# KO_0 vs. KO_20 
up_BP_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "up")
save_GO_results(up_BP_ko_20_vs_0_GO, "up")
down_BP_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "down")
save_GO_results(down_BP_ko_20_vs_0_GO, "down")

up_MF_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_ko_20_vs_0_GO, "up")
down_MF_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_ko_20_vs_0_GO, "down")

up_CC_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_ko_20_vs_0_GO, "up")
down_CC_ko_20_vs_0_GO <- GO_results(df_dds_ko_20_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_ko_20_vs_0_GO, "down")



# KO_0 vs. KO_40 
up_BP_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "up")
save_GO_results(up_BP_ko_40_vs_0_GO, "up")
down_BP_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "down")
save_GO_results(down_BP_ko_40_vs_0_GO, "down")

up_MF_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_ko_40_vs_0_GO, "up")
down_MF_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_ko_40_vs_0_GO, "down")

up_CC_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_ko_40_vs_0_GO, "up")
down_CC_ko_40_vs_0_GO <- GO_results(df_dds_ko_40_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_ko_40_vs_0_GO, "down")



# KO_0 vs. KO_60 
up_BP_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "up")
save_GO_results(up_BP_ko_60_vs_0_GO, "up")
down_BP_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "down")
save_GO_results(down_BP_ko_60_vs_0_GO, "down")

up_MF_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_ko_60_vs_0_GO, "up")
down_MF_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_ko_60_vs_0_GO, "down")

up_CC_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_ko_60_vs_0_GO, "up")
down_CC_ko_60_vs_0_GO <- GO_results(df_dds_ko_60_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_ko_60_vs_0_GO, "down")



# KO_0 vs. KO_80 
up_BP_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "up")
save_GO_results(up_BP_ko_80_vs_0_GO, "up")
down_BP_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "down")
save_GO_results(down_BP_ko_80_vs_0_GO, "down")

up_MF_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "up", ont_category = "MF")
save_GO_results(up_MF_ko_80_vs_0_GO, "up")
down_MF_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "down", ont_category = "MF")
save_GO_results(down_MF_ko_80_vs_0_GO, "down")

up_CC_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "up", ont_category = "CC")
save_GO_results(up_CC_ko_80_vs_0_GO, "up")
down_CC_ko_80_vs_0_GO <- GO_results(df_dds_ko_80_vs_0, "down", ont_category = "CC")
save_GO_results(down_CC_ko_80_vs_0_GO, "down")

count_GO_terms() Function

2/25/25

The following function produces a CSV file/table that contains the count of gene ontology description terms from the BP, MF, and CC annotations. It does so by parsing through the associated “up” and “down” regulated gene annotation files and inserts the values into their appropriate rows/columns. It looks for patterns denoted by the title of the CSV files saved in the previous step of annotating genes. It searches a specific directory (my personal one is the default) and searches for more specific directories that separated the “up” and “down” results (the default are set to my naming convention). The tables will be saved to their own new folder (named GO_term_counts) within your chosen data_file_path directory.

  • Regardless if you have this entire repository, and don’t need to specify any different file paths, you must include the CSV title pattern unique to each file produced during the annotating genes step. Those names are not dynamic, therefore, it will depend completely on how you named them yourself (assuming you changed them from what I originally had them as); BE CAREFUL! I suggest you just use the code as is as things are getting complicated to a point I’m struggling to make everything dynamic.

The function’s arguments are:

  • data_file_path = the path to the parent directory that contains your up-regulated and down-regulated gene annotation results (for BP, MF, and CC).
  • comparison_pattern = the particular pattern (i.e. part of the CSV file name) of the gene annotation results
    • Be sure to not include a beginning or ending underscore (the function accounts for that already).
  • up_regulated_data = the name of the child directory that contains your up-regulated data files
  • down_regulated_data = the name of the child directory that contains your down-regulated data files

Note: there is a lot of debugging code in this function to see how it works as I struggled writing it for quite some time.

count_GO_terms <- function(data_file_path = "../data/arabidopsis_GO_results", 
                           comparison_pattern, 
                           up_regulated_data = "up_results", 
                           down_regulated_data = "down_results") {
  # define the GO categories
  go_categories <- c("BP", "MF", "CC")
  
  # initialize the results matrix 
  results_matrix <- matrix(0, nrow = length(go_categories), ncol = 2, 
                           dimnames = list(go_categories, c("up", "down")))
  
  # helper function used to process files in a given folder and direction ("up" or "down")
  process_files <- function(folder, direction, results_matrix) {
    for (category in go_categories) {
      # build the exact file name pattern
      file_pattern <- paste0(direction, "_", category, "_", comparison_pattern, "_GO.csv") # very exact to the naming convention used for the annotating genes step 
      
      # list out files matching the name pattern (should be only one per category (BP, MF, CC))
      files <- list.files(folder, pattern = file_pattern, full.names = TRUE)
      
      if (length(files) > 0) {
        file <- files[1]  # take the initial match (should only be one anyways)
        
        # read the matching CSV file
        data <- read.csv(file, stringsAsFactors = FALSE)
        
        # count rows where p.adjust < 0.05 (will match the number of significant GO Description terms); ignores NA values 
        count <- sum(data$p.adjust < 0.05, na.rm = TRUE)
        
        # DEBUG: check the count and where it is being assigned
        print(paste("Updating matrix for category:", category, "and direction:", direction))
        print(paste("Count to assign:", count))
        
        # store result in matrix
        if (direction == "up") {
          results_matrix[category, "up"] <- count
        } else if (direction == "down") {
          results_matrix[category, "down"] <- count
        }
        
        # DEBUG: display the matrix after each count assignment
        print(results_matrix)  
      } else {
        # if no files are found (i.e. incorrect pattern input), print a warning error 
        warning(paste("No matching file found for", direction, category, "in", folder))
      }
    }
    return(results_matrix)  # return updated matrix
  }
  
  # define the output path
  output_dir <- file.path(data_file_path, "GO_term_counts")
  
  # create the directory (if it doesn't already exist)
  dir.create(output_dir, showWarnings = FALSE)
  
  # process both the "up" and "down" CSV file results 
  results_matrix <- process_files(file.path(data_file_path, up_regulated_data), "up", results_matrix)
  results_matrix <- process_files(file.path(data_file_path, down_regulated_data), "down", results_matrix)
  
  # DEBUG: display the final results matrix
  print("Final results_matrix:")
  print(results_matrix)
  
  # define the output path and save the results once the entire matrix is complete 
  output_file <- file.path(output_dir, paste0(comparison_pattern, "_GO_counts.csv"))
  write.csv(as.data.frame(results_matrix), output_file, row.names = TRUE)
}

The following code chunks are the running of the count_GO_terms() function to produce a table for each comparison.

  • The initial code chunk will display what the assignment to the matrix looks like in the terminal. The rest of the instances will hide the output.

KO Treatment vs. WT Treatment:

# WT_20 vs. KO_20 
count_GO_terms(comparison_pattern = "ko_20_vs_wt_20")
## [1] "Updating matrix for category: BP and direction: up"
## [1] "Count to assign: 72"
##    up down
## BP 72    0
## MF  0    0
## CC  0    0
## [1] "Updating matrix for category: MF and direction: up"
## [1] "Count to assign: 13"
##    up down
## BP 72    0
## MF 13    0
## CC  0    0
## [1] "Updating matrix for category: CC and direction: up"
## [1] "Count to assign: 22"
##    up down
## BP 72    0
## MF 13    0
## CC 22    0
## [1] "Updating matrix for category: BP and direction: down"
## [1] "Count to assign: 112"
##    up down
## BP 72  112
## MF 13    0
## CC 22    0
## [1] "Updating matrix for category: MF and direction: down"
## [1] "Count to assign: 6"
##    up down
## BP 72  112
## MF 13    6
## CC 22    0
## [1] "Updating matrix for category: CC and direction: down"
## [1] "Count to assign: 39"
##    up down
## BP 72  112
## MF 13    6
## CC 22   39
## [1] "Final results_matrix:"
##    up down
## BP 72  112
## MF 13    6
## CC 22   39
# WT_40 vs. KO_40 
count_GO_terms(comparison_pattern = "ko_40_vs_wt_40")

# WT_60 vs. KO_60
count_GO_terms(comparison_pattern = "ko_60_vs_wt_60")

# WT_80 vs. KO_80
count_GO_terms(comparison_pattern = "ko_80_vs_wt_80")

Only Controls:

# WT_0 vs. KO_0
count_GO_terms(comparison_pattern = "control_ko_0_vs_wt_0")

Only Wild-Type Samples:

# WT_0 vs. WT_20 
count_GO_terms(comparison_pattern = "wt_20_vs_0")

# WT_0 vs. WT_40 
count_GO_terms(comparison_pattern = "wt_40_vs_0")

# WT_0 vs. WT_60 
count_GO_terms(comparison_pattern = "wt_60_vs_0")

# WT_0 vs. WT_80 
count_GO_terms(comparison_pattern = "wt_80_vs_0")

Only Knockout-Line Samples:

# KO_0 vs. KO_20 
count_GO_terms(comparison_pattern = "ko_20_vs_0")

# KO_0 vs. KO_40 
count_GO_terms(comparison_pattern = "ko_40_vs_0")

# KO_0 vs. KO_60 
count_GO_terms(comparison_pattern = "ko_60_vs_0")

# KO_0 vs. KO_80 
count_GO_terms(comparison_pattern = "ko_80_vs_0")

plot_top_GO_terms() Function

Double check that this works correctly by looking at the values directly; I think it struggles when there are matching values

The following function uses a similar logic as the count_GO_terms() function. The difference is that instead of making a table with significant GO term counts, it makes a horizontal bar plot that displays the most significant GO terms for each category (BP, MF, CC) across both the up and down regulated gene annotations for a particular comparison. In other words, it parses through all associated data sets for the chosen comparison_pattern and finds the smallest p.adjust values across the up and down regulated genes combined. It then separates them by category.

The function’s arguments are the same as the count_GO_terms() function, however, there is now an option to select how many significant terms we want in the plot.

  • top_n = how many significant terms we want to include in the plot (top 3, top 5, top 7, top 10, etc.)
    • Default is 5
plot_top_GO_terms <- function(data_file_path = "../data/arabidopsis_GO_results", 
                              comparison_pattern, 
                              top_n = 5,
                              up_regulated_data = "up_results", 
                              down_regulated_data = "down_results") {
  
  # define the GO categories 
  go_categories <- c("BP", "MF", "CC")
  all_data <- data.frame()  # initialize a data frame to store all GO term info
  
  # helper function used to process files in a given folder and direction ("up" or "down")
  process_files <- function(folder, direction) {
    category_data <- data.frame()  # initialize a data frame for the category info
    
    for (category in go_categories) {
      # build the exact file name pattern 
      file_pattern <- paste0(direction, "_", category, "_", comparison_pattern, "_GO.csv")
      # list out files matching the name pattern (should be only one per category (BP, MF, CC))
      files <- list.files(folder, pattern = file_pattern, full.names = TRUE)
      
      # take the initial match (should only be one anyways)
      if (length(files) > 0) {
        data <- read.csv(files[1], stringsAsFactors = FALSE)
        
        # filter the data only keep non-NA p.adjust values and create Category and Regulation columns
        data <- data |>
          dplyr::filter(!is.na(p.adjust)) |>
          dplyr::mutate(Category = category, Regulation = direction)
        
        # bind the data for a category to the category_data data frame
        category_data <- dplyr::bind_rows(category_data, data)
      } else {
        # if no files are found (i.e. incorrect pattern input), print a warning error 
        warning(paste("No matching file found for", direction, category, "in", folder))
      }
    }
    return(category_data)  # return the collected data for the given direction
  }
  
  # process both the "up" and "down" CSV file results 
  up_data <- process_files(file.path(data_file_path, up_regulated_data), "up")
  down_data <- process_files(file.path(data_file_path, down_regulated_data), "down")
  
  # combine the up-regulated and down-regulated data into a single data frame (in the form of all_data)
  all_data <- dplyr::bind_rows(up_data, down_data)
  
  # if no significant (< 0.05) GO terms are found, display error 
  if (nrow(all_data) == 0) {
    stop("No significant GO terms found for given comparison pattern.")
  }
  
  # select the top 5 GO terms 
  top_go_data <- all_data |>
    group_by(Category) |>
    arrange(p.adjust) |>  # sort by smallest p.adjust to get the most significant terms (ascending order)
    slice_head(n = top_n) |>   # select the top GO terms per category (choose how many when calling the function)
    ungroup() |>
    mutate(Label = paste0(ID, ": ", Description))  # create a label for each GO term (ID: Description)
  
  # plot the top GO terms 
  ggplot(top_go_data, aes(x = reorder(Label, p.adjust), y = -log10(p.adjust), fill = Regulation)) +
    geom_col(position = position_dodge(width = 0.7)) +  # use position_dodge to place up/down bars side by side
    coord_flip() +  # flip coordinates so the bars are horizontal
    facet_grid(Category ~ ., scales = "free_y", space = "free") +  # categories grouped together with free y-scales
    scale_fill_manual(values = c("up" = "lightblue", "down" = "pink")) +  # set colors for up and down regulation 
    labs(title = paste(top_n, "Most Significant GO Terms Across ONT Categories for", comparison_pattern),
         x = "GO Term (ID: Description)", 
         y = "-log10(p.adjust)", 
         fill = "Regulation") +
    theme_minimal() +
    theme(strip.text.y = element_text(angle = 0))  # keep facet labels horizontal (for categories)
}

Example run using the control group comparison:

plot_top_GO_terms(comparison_pattern = "control_ko_0_vs_wt_0", top_n = 5)

9) DEG Visualization

The following code is going to explore the various results stemming from our differential gene expression analysis through a few visualization techniques.

Heat Map

3/18/25

Using the a dds object comparison and its results object produced earlier in the script, we will be making a heat map that demonstrates the pattern regarding the highest 50 and lowest 50 differential expressed genes according to their log2FoldChange value. This is simply to visualize the pattern.

For this initial example, we will be using dds_controls and res_dds_controls which are the dds objects comparing ko_0 vs. wt_0. I mainly started with this because it did not need any further contrasting but if we want to test more we would have to contrast accordingly to the samples.

This heat map filters out for padj < 0.05 and omits the NA values found in the padj column:

This heat map lacks filtering for padj < 0.05 (i.e. includes everything):

Should we filter the padj or leave it as is? Should we use normalized counts? I’m assuming yes!

KEGG Pathway Analysis

3/17/25

The following code will utilize KEGG Pathway Analysis to both find and visualize certain biological pathways and how they are affected by the up and down regulation of certain genes.

Use the following packages (as well as the others used in this script so far) to conduct this analysis.

library(DOSE)
library(pathview)

# search database to make sure our organism is actually available (newsflash: it is)
search_kegg_organism('Arabidopsis thaliana', by = 'scientific_name')
kegg_code scientific_name common_name
565 ath Arabidopsis thaliana thale cress

IGNORE THE SECTION TITLED ‘CONVERT TAIR IDS TO ENTREZ IDS’ BECAUSE ACCORDING TO THE FOLLOWING:

##                ktax.id                 tax.id              kegg.code 
##               "T00041"                 "3702"                  "ath" 
##        scientific.name            common.name          entrez.gnodes 
## "Arabidopsis thaliana"          "thale cress"                    "0" 
##            kegg.geneid            ncbi.geneid         ncbi.proteinid 
##            "AT2G23820"               "816914"            "NP_179962" 
##                uniprot 
##               "F4IMN2"

WE ACTUALLY DO NOT NEED THE ENTREZID IN ORDER TO CONDUCT KEGG PATHWAY ANALYSIS USING ENRICHKEGG() AND CLUSTERPROFILER FOR ARABIDOPSIS!

Convert TAIR IDs to ENTREZ IDs (IGNORE THIS)

THE FOLLOWING CODE IS SIMPLY FOR REFERENCE:

# choose any of the Deseq2 resulting data sets (I'm testing knockout-line vs. wild-type controls)

# copy original data frame and remove tailing ".1" to match TAIR format 
df_dds_controls_ko_vs_wt_entrez <- df_dds_controls_knockout_line_vs_wild_type |>
  mutate(GeneID = sub("\\.\\d+$", "", GeneID))

# extract the GeneID column
tair_ids_controls <- df_dds_controls_ko_vs_wt_entrez$GeneID

# convert TAIR IDs to ENTREZ IDs
entrez_mapping_controls <- select(org.At.tair.db, keys = tair_ids_controls, keytype = "TAIR", columns = "ENTREZID")

# when matching TAIR IDs to ENTREZ IDs, a warning denotes that there are many:1 mapping instances of TAIR to ENTREZ (i.e. various instances of the same TAIR values being matched to the same ENTREZ values). This is due to transcript variety (.1 vs. .3, but I think that should be fine). Is it? 
entrez_mapping_controls |>
  dplyr::count(TAIR) |>
  dplyr::filter(n > 1)

# merge with newly made _entrez data frame to keep track of IDs
df_dds_controls_ko_vs_wt_entrez <- merge(df_dds_controls_ko_vs_wt_entrez, 
                                         entrez_mapping_controls, 
                                         by.x = "GeneID", by.y = "TAIR", all.x = TRUE)

# move the newly created ENTREZID next to the GeneID column and remove duplicates 
df_dds_controls_ko_vs_wt_entrez <- df_dds_controls_ko_vs_wt_entrez |>
  dplyr::select(GeneID, ENTREZID, everything()) |>
  distinct() # collapsed a few duplicate entries due to many:1 mapping 

# View(df_dds_controls_ko_vs_wt_entrez)

IGNORE THIS AS WELL!

# add a new column (regulation) that signifies UP or DOWN according to the log2FoldChange 
df_dds_controls_ko_vs_wt_entrez <- df_dds_controls_ko_vs_wt_entrez |>
  mutate(regulation = case_when(
    log2FoldChange > 0 ~ "UP",
    log2FoldChange < 0 ~ "DOWN"
  ))

# omits rows with NA values in the ENTREZID column 
df_dds_controls_ko_vs_wt_entrez <- df_dds_controls_ko_vs_wt_entrez[!is.na(df_dds_controls_ko_vs_wt_entrez$ENTREZID), ]

Prepare Gene List

# select whichever Deseq2 results you like (I'm testing knockout-line vs. wild-type controls)

# remove tailing ".1" to match TAIR format (remember: padj is already < 0.05 so we don't have to filter)
dds_kegg_controls <- df_dds_controls_knockout_line_vs_wild_type |>
  mutate(GeneID = sub("\\.\\d+$", "", GeneID))

# prepare the gene list for KEGG analysis
geneList_controls <- dds_kegg_controls$log2FoldChange # makes vector with log2FoldChange values 
names(geneList_controls) <- dds_kegg_controls$GeneID # adds GeneID to the vector 

KEGG Pathway Over-Representation Analysis

# run KEGG enrichment analysis on the entire (both up and down regulated genes) geneList_controls and produce a list of over-represented pathways we can then visualize in the next step 
kegg_ora_analysis_controls <- enrichKEGG(gene = names(geneList_controls),
                                     organism     = 'ath', 
                                     pvalueCutoff = 0.05)

# View(kegg_ora_analysis_controls)

We can then visualize one of the particular pathways:

  • I’M STUCK HERE! Why isn’t it mapping to an ID when the ORA analysis clearly shows this specific pathway is in it and also can find it via browseKEGG()? WHY ISN’T THIS WORKING?“

Remove ‘eval = FALSE’ when it starts working . . .

# use pathview() to visualize the pathway 
pathview(gene.data     = geneList_controls, # input gene list (log2FoldChange)
         pathway.id    = "ath00500", # pathway ID we want to visualize
         species       = "ath",         # arabidopsis species 
         out.suffix    = "pathway")

# can use the following to verify if the actual pathway even exists
# browseKEGG(kegg_analysis_controls, 'ath00500')

KEGG Pathway Gene Set Enrichment Analysis

THE FOLLOWING IS NOT WORKING…or is it:

# remove duplicates by keeping the first occurrence
geneList_controls_unique <- geneList_controls[!duplicated(names(geneList_controls))]

# sort the gene list in decreasing order of log2FoldChange
geneList_controls_sorted <- sort(geneList_controls_unique, decreasing = TRUE)

# NOT WORKING!!!
# run KEGG pathway gene set enrichment analysis 
kegg_gsea_controls <- gseKEGG(geneList     = geneList_controls_sorted,
                              organism     = 'ath',
                              pvalueCutoff = 0.05) 

Confirming or Contradicting GO Analysis using plotCounts()

3/18/25

The following function, plot_gene_counts(), is a makeshift function that takes arguements and feeds them into the Deseq2 built-in function called plotCounts(). This allows us to visualize specific genes and their counts found in specific dds objects and contrasts. It takes a few arguements:

  • dds = the dds object you will be using (not the results but a DESeqDataSet)
  • genes = one or more genes (just make sure to use a list for multiple) of your choosing found in the dds
  • intgroup = a specific column or condition that was found in colData that we want to differentiate by
  • filter_groups = the specific groups found within intgroup that you wish to contrast

These parameters allow us to use the few dds objects made earlier in this script and stil contrast specific samples accordingly.

The function can take a single gene or a list of genes and will simply facet wrap it so that we can avoid making a ton of unnecessary plots for the same contrast conditions and varying genes. I would only suggest to do 2 or 3 genes at a time.

  • Note: because of the way the x-axis and the points are handled, the plot of a single gene make look slightly different to its version within a facetted plot. This is simply the effect of moving the points in different positions on the x-axis, something that actually has no bearing on the y-axis positions and thus retains the integrity of the count and plotting structure. At a quick glance, it may seem a bit off but don’t stress, it is the exact same; don’t believe me? Compare and contrast!

In this particular script, the choice of re-leveled dds objects we have are:

  • dds_controls (control samples)
  • dds_treatment (treatment vs. treatment samples of both wild-type and knockout-line)
  • dds_ko (only knockout-line treatment samples)
  • dds_wt (only wild-type treatment samples)

They are also found in the title of each plot distinguishing the plots from one another (especially for dds_ko and dds_wt which only use numbers for their x-axis).

Use any of these and specify according to a particular gene, intgroup which is a colData column used in the original contrast (group vs. treatment vs. genotype), and specific samples for filter_groups (wt_0 vs. ko_0).

Compare the clustering of points to the log2FoldChange of your chosen gene(s). Remember that the log2FoldChange represents up or down regulation relative to the reference level which has been set as the wild-type control, wild-type variant, or knockout-line control where appropriate. Then, physically look at the log2FoldChange value and if, for example, it is negative, make sure its clustering is lower than the reference level clustering on the graph. Vice versa if positive. If both sets of clustering are close together, that is indicative of a fairly small padj value. We are just trying to make sure that the log2FoldChange is accurately demonstrating up or down regulation via the normalized counts.

  • do 2 or 3 genes per dds object and contrast

Lastly, I’ve included a red dot that is representative of the mean value between the triplicate for each sample. You could remove that if you’d like.

Should I use normalized counts like plotCounts() normally does or should I use the raw counts as accomplished by changing: plot_data <- plotCounts(dds, gene = “AT1G02280.1”, intgroup = “group”, returnData = TRUE, normalized = FALSE)

plot_gene_counts <- function(dds, genes, intgroup, filter_groups) {
  # pull name of the dds object
  dds_name <- deparse(substitute(dds))
  
  # extract plot data for multiple genes
  plot_data_list <- lapply(genes, function(gene) {
    plot_data <- plotCounts(dds, gene = gene, intgroup = intgroup, returnData = TRUE)
    plot_data$gene <- gene  # Add gene identifier
    plot_data
  })
  
  # combine into a single data frame
  plot_data_combined <- bind_rows(plot_data_list)
  
  # filter based on specified groups
  plot_data_filtered <- plot_data_combined |>
    filter(.data[[intgroup]] %in% filter_groups)

  # generate plot title dynamically
  plot_title <- paste("Gene Counts for", paste(filter_groups, collapse = " vs. "),
                      "\nData from:", dds_name)
  
  # create the plot with facet_wrap
  ggplot(plot_data_filtered, aes_string(x = intgroup, y = "count")) +
    geom_jitter(width = 0.2, size = 3, alpha = 0.6) +
    stat_summary(fun = "mean", geom = "point", size = 4, color = "red") +
    theme_minimal(base_size = 14) +
    labs(title = plot_title, y = "Normalized Counts", x = "Condition") +
    facet_wrap(~gene, scales = "free_y") +
    theme(
      strip.background = element_rect(fill = "lightgray", color = "black", size = 1.5),  # Highlight facet labels
      strip.text = element_text(face = "bold", size = 12),  # Make facet labels bold
      panel.border = element_rect(color = "black", fill = NA, size = 1)  # Add borders to panels
    )
}

# example when using a single gene from dds_treatment (ko_20 vs. wt_20)
plot_gene_counts(dds_treatment, genes = "AT1G19350.4", 
                 intgroup = "group", filter_groups = c("ko_20", "wt_20"))

# example when using multiple genes from dds_treatment (ko_20 vs. wt_20)
plot_gene_counts(dds_treatment, genes = c("AT1G02280.1", "AT1G19350.4"), 
                 intgroup = "group", filter_groups = c("ko_20", "wt_20"))

Since we’ve already done 2 genes for ko_20 vs. wt_20 above, let’s continue and do at least 2 genes for each contrast we’ve done thus far.

The remaning KO Treatment vs. WT Treatment contrasts:

Only Controls:

# code for if you ever want to view the normalized counts used for plotting the graph of a specific gene and compare to plot; I've compared and it has looked great so far
counts(dds_controls, normalized = TRUE)["AT1G02640.1", ] # specific to a particular gene 
## Col01_1_1 Col01_1_2 Col01_1_3  ST35_1_1  ST35_1_2  ST35_1_3 
##  5674.450  5194.160  5408.201  2042.590  2755.125  3249.671

Only Wild-Type Samples:

Only Knockout-Line Samples:

LS0tCnRpdGxlOiAiUk5BLXNlcSBBbmFseXNpcyBvbiBBcmFiaWRvcHNpcyIKYXV0aG9yOiAiRXJpY2sgQWx2YXJleiIKZGF0ZTogIkphbnVhcnkgMTQsIDIwMjUiCm91dHB1dDogCiAgICBodG1sX2RvY3VtZW50OgogICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgICB0b2M6IHRydWUKICAgICAgICB0b2NfZmxvYXQ6CiAgICAgICAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAgICAgZGZfcHJpbnQ6IGthYmxlCiAgICAgICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICAgICAgbW9kZTogc2VsZmNvbnRhaW5lZAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY2FjaGUgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCmBgYAoKVGhlIGZvbGxvd2luZyBhcmUgdGhlIG5lY2Vzc2FyeSBSLXBhY2thZ2VzIGZvciBydW5uaW5nIHRoZSBjb2RlIGJlbG93OiAKYGBge3IsIHJlc3VsdHMgPSAiaGlkZSJ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKIyBiZSBzdXJlIHRvIHJ1biBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpIGFuZCB1c2UgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhY2thZ2UiKSBvcmRlciB0byBpbnN0YWxsIGFuZCBydW4gdGhlIGZvbGxvd2luZyBsaWJyYXJpZXMgCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGFwZWdsbSkKbGlicmFyeShhc2hyKQpsaWJyYXJ5KHBoZWF0bWFwKQpgYGAKCiMgMSkgUGVwYXJpbmcgQ291bnQgRGF0YSAKKioxLzIwLzI1KioKQmVsb3cgaXMgYSBzbmlwcGV0IG9mIGZlYXR1cmUgY291bnQgZGF0YSBvZiBBcmFiaWRvcHNpcyAoYm90aCBjb250cm9sIGFuZCB0cmVhdGVkKSByZWdhcmRpbmcgaGVhdnkgbWV0YWxzLgpgYGB7ciwgZWNobyA9IEZBTFNFfQojIHJlYWRzIGluIGNvdW50IGRhdGEgCmZlYXR1cmVfY291bnRzIDwtIHJlYWRfdHN2KCIuLi9kYXRhL2ZlYXR1cmVDb3VudHNfYXRfU1QzNS50eHQiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKIyBzaG93cyBhIHNuaXBwZXQgb2YgdGhlIGNvdW50IGRhdGEgCmhlYWQoZmVhdHVyZV9jb3VudHMsIDEwKQoKYGBgCgpJIGNyZWF0ZWQgYSBkYXRhIGZyYW1lIHRoYXQgbWF0Y2hlcyB0aGUgc2FtcGxlIGluZm8gcHJvdmlkZWQgYnkgU2FuanUncyBQb3dlclBvaW50IHNsaWRlIGFzIHNob3duIGhlcmU6CmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuLi9yZXNvdXJjZXMvYXJhYmlkb3BzaXNfc2FtcGxlX2tleS5wbmciKQoKYGBgCgpUaGUgZm9sbG93aW5nIGNvZGUgY3JlYXRlcyB0aGUgZGF0YSBmcmFtZSBpdHNlbGYsIHdoaWNoIHdpbGwgYmUgdXNlZCBhcyBjb2xEYXRhIGluIERFU2VxMi4KYGBge3IsIHJlc3VsdHMgPSAiaGlkZSJ9CiMgZXh0cmFjdCBjb2x1bW4gbmFtZXMgZm9yIHRoZSBzYW1wbGVzIChleGNsdWRlIG5vbi1zYW1wbGUgY29sdW1ucykKc2FtcGxlX25hbWVzIDwtIGNvbG5hbWVzKGZlYXR1cmVfY291bnRzKVstYygxLCAyKV0gICMgIkdlbmVJRCIgYW5kICJMZW5ndGgiIGFyZSB0aGUgZmlyc3QgdHdvIGNvbHVtbnMgKG5vbi1zYW1wbGUgY29sdW1ucykKCiMgbWFwIHNhbXBsZSBuYW1lcyB0cmVhdG1lbnQgYW5kIGdlbm90eXBlIGNvbHVtbnMsIHJlc3BlY3RpdmVseSAKY29sRGF0YSA8LSBkYXRhLmZyYW1lKAogIHNhbXBsZV9uYW1lID0gc2FtcGxlX25hbWVzLAogIHRyZWF0bWVudCA9IGNhc2Vfd2hlbigKICAgIGdyZXBsKCJeQ29sMDFfMSIsIHNhbXBsZV9uYW1lcykgfiAiMCIsCiAgICBncmVwbCgiXkNvbDAxXzIiLCBzYW1wbGVfbmFtZXMpIH4gIjIwIiwKICAgIGdyZXBsKCJeQ29sMDFfMyIsIHNhbXBsZV9uYW1lcykgfiAiNDAiLAogICAgZ3JlcGwoIl5Db2wwMV80Iiwgc2FtcGxlX25hbWVzKSB+ICI2MCIsCiAgICBncmVwbCgiXkNvbDAxXzUiLCBzYW1wbGVfbmFtZXMpIH4gIjgwIiwKICAgIGdyZXBsKCJeU1QzNV8xIiwgc2FtcGxlX25hbWVzKSB+ICIwIiwKICAgIGdyZXBsKCJeU1QzNV8yIiwgc2FtcGxlX25hbWVzKSB+ICIyMCIsCiAgICBncmVwbCgiXlNUMzVfMyIsIHNhbXBsZV9uYW1lcykgfiAiNDAiLAogICAgZ3JlcGwoIl5TVDM1XzQiLCBzYW1wbGVfbmFtZXMpIH4gIjYwIiwKICAgIGdyZXBsKCJeU1QzNV81Iiwgc2FtcGxlX25hbWVzKSB+ICI4MCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXyAjIGRlZmF1bHQgaWYgbm8gcGF0dGVybiBtYXRjaGVzCiAgICApLAogIGdlbm90eXBlID0gY2FzZV93aGVuKAogICAgZ3JlcGwoIkNvbCIsIHNhbXBsZV9uYW1lcywgaWdub3JlLmNhc2UgPSBUUlVFKSB+ICJ3aWxkX3R5cGUiLAogICAgZ3JlcGwoIlNUMzUiLCBzYW1wbGVfbmFtZXMsIGlnbm9yZS5jYXNlID0gVFJVRSkgfiAia25vY2tvdXRfbGluZSIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXyAjIGRlZmF1bHQgaWYgbm8gcGF0dGVybiBtYXRjaGVzCiAgICApLAogIHJvdy5uYW1lcyA9IHNhbXBsZV9uYW1lcyAjIHNldCBzYW1wbGVfbmFtZSBjb2x1bW4gY29udGVudCBhcyByb3cgbmFtZXMKICApCgojIGNvbWJpbmUgJ2dlbm90eXBlJyBhbmQgJ3RyZWF0bWVudCcgaW50byBhIG5ldyAnZ3JvdXAnIGNvbHVtbiB3aXRoIHNob3J0ZW5lZCBnZW5vdHlwZXMKY29sRGF0YSRncm91cCA8LSBwYXN0ZSgKICBjYXNlX3doZW4oCiAgICBjb2xEYXRhJGdlbm90eXBlID09ICJ3aWxkX3R5cGUiIH4gInd0IiwgCiAgICBjb2xEYXRhJGdlbm90eXBlID09ICJrbm9ja291dF9saW5lIiB+ICJrbyIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXwogICksCiAgY29sRGF0YSR0cmVhdG1lbnQsCiAgc2VwID0gIl8iCikKCiMgY29udmVydCBhcHByb3ByaWF0ZSBjb2x1bW5zIGludG8gZmFjdG9ycyBhbmQgcmVtb3ZlIHNhbXBsZV9uYW1lIGNvbHVtbiAKY29sRGF0YSA8LSBjb2xEYXRhIHw+CiAgbXV0YXRlKAogICAgdHJlYXRtZW50ID0gZmFjdG9yKHRyZWF0bWVudCksCiAgICBnZW5vdHlwZSA9IGZhY3RvcihnZW5vdHlwZSksCiAgICBncm91cCA9IGZhY3Rvcihncm91cCkKICAgICkgfD4KICBzZWxlY3QoLXNhbXBsZV9uYW1lKSAjIHJlbW92ZSBzYW1wbGVfbmFtZSBjb2x1bW4gCgojIHZlcmlmeSBkYXRhIHN0cnVjdHVyZSAKc3RyKGNvbERhdGEpCgpgYGAKCmBgYCB7ciwgZWNobyA9IEZBTFNFfQojIGRpc3BsYXkgY29sRGF0YQpoZWFkKGNvbERhdGEsIDEwKQoKYGBgCgpMZXQncyB2ZXJpZnkgdGhhdCB0aGUgcm93IG5hbWVzIGluIGNvbERhdGEgbWF0Y2hlcyB0aGUgY29sdW1uIG5hbWVzIGluIGZlYXR1cmVfY291bnRzIGFuZCBhcmUgaW4gdGhlIHNhbWUgb3JkZXIuCmBgYHtyfQojIGNoZWNraW5nIGZvciBtYXRjaGluZyBuYW1lcyAKYWxsKGNvbG5hbWVzKGZlYXR1cmVfY291bnRzKSAlaW4lIHJvd25hbWVzKGNvbERhdGEpKQoKYGBgCgpUaGUgcmVzdWx0IGlzIEZBTFNFOyB0aGlzIGlzIGR1ZSB0byB0aGUgaW5pdGlhbCB0d28gY29sdW1ucyBmb3VuZCBpbiB0aGUgZmVhdHVyZV9jb3VudHMgZm9yIEdlbmVJRCBhbmQgTGVuZ3RoLiBMZXQncyByZW1vdmUgdGhlIExlbmd0aCBjb2x1bW4gYW5kIGNvbnZlcnQgdGhlIEdlbmVJRCBjb2x1bW4gaW50byB0aGUgcm93IG5hbWVzIG9mIHRoaXMgZGF0YSBmcmFtZSAoaXQgc2hvdWxkbid0IGludGVyZmVyZSB3aXRoIHRoZSBERVNlcTIgb3V0cHV0KSEgCmBgYHtyfQojIGNvbnZlcnRpbmcgR2VuZUlEIHRvIHJvd25hbWVzIGFuZCByZW1vdmluZyBMZW5ndGggY29sdW1uIGZvdW5kIGluIG9yaWdpbmFsIGZlYXR1cmUgY291bnQgZGF0YSBzZXQgCmRhdGFfY291bnRzIDwtIGZlYXR1cmVfY291bnRzIHw+CiAgY29sdW1uX3RvX3Jvd25hbWVzKCJHZW5lSUQiKSB8PgogIHNlbGVjdCgtTGVuZ3RoKQoKIyBjaGVja2luZyBmb3IgbWF0Y2hpbmcgbmFtZXMgCmFsbChjb2xuYW1lcyhkYXRhX2NvdW50cykgJWluJSByb3duYW1lcyhjb2xEYXRhKSkKCiMgY2hlY2tpbmcgdGhhdCB0aGV5IGFyZSBpbiB0aGUgc2FtZSBvcmRlcgphbGwoY29sbmFtZXMoZGF0YV9jb3VudHMpID09IHJvd25hbWVzKGNvbERhdGEpKQoKIyB2aWV3IG5ldyBkYXRhIGNvdW50cyBmb3JtYXQKaGVhZChkYXRhX2NvdW50cywgMTApCgpgYGAKQXMgd2UgY2FuIHNlZSwgaXQgbm93IHJldHVybnMgVFJVRSBmb3IgYm90aCBjb25kaXRpb25zIChtYXRjaGluZyBuYW1lcyBhbmQgc2FtZSBvcmRlcikuCgojIDIpIENvbnN0cnVjdCBhIERFU2VxRGF0YVNldCBPYmplY3QKKioxLzIxLzI1KioKCldlIHdpbGwgbm93IGJlIGNyZWF0aW5nIG91ciBERVNlcURhdGFTZXQgb2JqZWN0IHVzaW5nIHRoZSBERVNlcTIgcGFja2FnZSBhbmQgb3VyIHJlY2VudGx5IHVwZGF0ZWQgYW5kIG5ld2x5IGNyZWF0ZWQgZGF0YSBmcmFtZXMgdGl0bGVkIGRhdGFfY291bnRzIGFuZCBjb2xEYXRhLCByZXNwZWN0aXZlbHkuIAoKYGBge3J9CiMgY3JlYXRpbmcgREVTZXEyIG9iamVjdCAKZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gZGF0YV9jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiB0cmVhdG1lbnQgKyBnZW5vdHlwZSArIHRyZWF0bWVudDpnZW5vdHlwZSkKCmRkcyAKCmBgYAogIApXZSBoYXZlIGEgdG90YWwgb2YgNDA3NDUgcm93cy4KCkxldCdzIG5vdyBwcmUtZmlsdGVyIHRoZSBkZHMgdG8gcmVtb3ZlIHRob3NlIGVudHJpZXMgdGhhdCBoYXZlIGxlc3MgdGhhbiAxMCByZWFkIGNvdW50cyAodGhpcyBpcyBhbiBhcmJpdHJhcnkgbnVtYmVyIEkndmUgc2ltcGx5IGNob3NlbiBhY2NvcmRpbmcgdG8gRHIuIExpYW5nJ3MgYWR2aWNlKS4KYGBge3J9CiMgcmVtb3ZlIGxvdyBnZW5lIGNvdW50cyAoPDEwKQprZWVwX2NvdW50cyA8LSByb3dTdW1zKGNvdW50cyhkZHMpKSA+PSAxMApkZHMgPC0gZGRzW2tlZXBfY291bnRzLF0KCmRkcwoKYGBgCldlIGhhdmUgZmlsdGVyZWQgZG93biB0byAyNzcxMCByb3dzLgoKIyAzKSBSZS1sZXZlbCBGYWN0b3JzIGZvciBUcmVhdG1lbnQgQ29tcGFyaXNvbnMgCioqMS8yMS8yNSoqCgpXZSBub3cgd2FudCB0byByZS1sZXZlbCBhbmQgY29tcGFyZSBkaWZmZXJlbnQgdHJlYXRtZW50IGNvbmRpdGlvbnMgdG8gb25lIGFub3RoZXIuIEhlcmUgYXJlIHRoZSBmb2xsb3dpbmcgdmFyaWF0aW9ucyBvZiB0aGUgY29tcGFyaXNvbnMgd2Ugd2lsbCBjb25kdWN0OiAKCiAgLSB3aWxkX3R5cGUgY29udHJvbCB2cy4ga25vY2tvdXRfbGluZSBjb250cm9sCiAgICAtIDAgdnMuIDAKICAKICAtIHdpbGRfdHlwZSB0cmVhdG1lbnQgdnMuIHdpbGRfdHlwZSBjb250cm9sIAogICAgLSBXVF8yMCB2cy4gV1RfMCwgV1RfNDAgdnMuIFdUXzAsIGV0Yy4KICAKICAtIGtub2Nrb3V0X2xpbmUgdHJlYXRtZW50IHZzLiBrbm9ja291dF9saW5lIGNvbnRyb2wgCiAgICAtIEtPXzIwIHZzLiBLT18wLCBLT180MCB2cy4gS09fMCwgZXRjLgogICAgCiAgLSBrbm9ja291dF9saW5lIHRyZWF0bWVudCB2cy4gd2lsZF90eXBlIHRyZWF0bWVudCAKICAgIC0gS09fMjAgdnMuIFdUXzIwLCBLT180MCB2cy4gV1RfNDAsIGV0Yy4KICAKTm90ZTogd2UgZG9uJ3QgaGF2ZSB0byBuZWNlc3NhcmlseSBzdGF0ZSB3aGljaCByZWZlcmVuY2UgbGV2ZWwgd2Ugd2FudCB0byB1c2UuIERFU2VxMiB3aWxsIHNpbXBseSBjaG9vc2UgYWxwaGFiZXRpY2FsbHkgd2hpY2ggbGV2ZWwgdG8gdXNlLgpgYGB7cn0KIyB1c2Ugd2lsZF90eXBlIGNvbnRyb2wgYXMgcmVmZXJlbmNlIGJhc2VsaW5lIApkZHMkdHJlYXRtZW50IDwtIHJlbGV2ZWwoZGRzJHRyZWF0bWVudCwgcmVmID0gIjAiKQpkZHMkZ2Vub3R5cGUgPC0gcmVsZXZlbChkZHMkZ2Vub3R5cGUsIHJlZiA9ICJ3aWxkX3R5cGUiKQojIHVzZSB0aGUgY29udHJvbCBhcyByZWZlcmVuY2UgZm9yIGV2ZXJ5IHNpbmdsZSBkZHMgb2JqZWN0IEkgd2lsbCBiZSBtYWtpbmcgCgojIHlvdSBtYXkgd2FudCB0byBzZXQgdGhpcyB0byBhIGRpZmZlcmVudCB2YXJpYWJsZSBvYmplY3Qgc28gdGhhdCB3ZSBjYW4ga2VlcCByZXVzaW5nIG91ciBzZXQgdXAgZGF0YSBhbmQgbm90IG92ZXJ3cml0ZSBpdCBldmVyeSB0aW1lIChkb3VibGUtY2hlY2sgd2l0aCBEci4gTGlhbmcgaWYgSSBjYW4gZG8gdGhhdCBiZWNhdXNlIHRoaXMgZG9lc24ndCBzZWVtIHQgbWFrZSBuZXcgdmFyaWFibGUgYW55d2F5KQpgYGAKCklNUE9SVEFOVCBOT1RFOiBNYWtlIHN1cmUgdG8gY29sbGFwc2UgdGhlIHRlY2huaWNhbCByZXBsaWNhdGVzIGJlZm9yZSBhbmFseXNpczsgaG93ZXZlciwgZG8gbm90IGNvbGxhcHNlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyEgKEkgZG9uJ3QgcmVhbGx5IGtub3cgd2hhdCB0aGlzIG1lYW5zIHRob3VnaCkKCiMgNCkgUnVuIERFU2VxMiAKKioxLzIxLzI1KioKCldlIGNhbiBub3cgcnVuIERFU2VxMiBhbmQgc2VlIGlmIG91ciBkYXRhIHN0cnVjdHVyZSB3b3JrcyEgCmBgYHtyfQojIHJ1biB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcwpkZHMgPC0gREVTZXEoZGRzKQoKIyB2aWV3IHdoYXQgY29uZGl0aW9ucyB3ZXJlIGNvbnRyYXN0ZWQgdG8gbWFrZSBzdXJlIHRoZXkgbWF0Y2ggd2hhdCB3ZSBhY3R1YWxseSBtZWFudCB0byBjb21wYXJlIApyZXN1bHRzTmFtZXMoZGRzKQoKIyBzdG9yZSB0aGUgcmVzdWx0cyBpbiBhbiBvYmplY3QgCnJlc19kZHMgPC0gcmVzdWx0cyhkZHMpCgojIHZpZXcgREVTZXEyIHJlc3VsdHMgCnJlc19kZHMKYGBgCgojIyBOb3JtYWxpemF0aW9uIAoqKjEvMjgvMjUqKgoKVGhlIGZvbGxvd2luZyBjb2RlIGNodW5rIGlzIHRoZSBub3JtYWxpemF0aW9uIG9mIHRoZSBkYXRhJ3MgcmVhZCBjb3VudHMgZm9yIGZ1dHVyZSB1c2U6CmBgYHtyfQojIG5vcm1hbGl6YXRpb24gb2YgcmVhZCBjb3VudHMgZm9yIGZ1dHVyZSB1c2UgCmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykKc2l6ZUZhY3RvcnMoZGRzKQpub3JtYWxpemVkX2NvdW50cyA8LSBjb3VudHMoZGRzLCBub3JtYWxpemVkID0gVFJVRSkKCiMgbWFrZSBhIGJveCBwbG90IHVzaW5nIG5vcm1hbGl6ZWQgY291bnRzIApib3hwbG90KGxvZzEwKG5vcm1hbGl6ZWRfY291bnRzKSkKCiMgd3JpdGUgZGF0YSB0byBhIGNzdiBmaWxlIAp3cml0ZS5jc3Yobm9ybWFsaXplZF9jb3VudHMsIGZpbGUgPSAiLi4vZGF0YS9zdDM1X25vcm1hbGl6ZWRfY291bnRzLmNzdiIpCmBgYAoKIyA1KSBFeHBsb3JlIERFU2VxMiBSZXN1bHRzCioqMS8yMS8yNSoqCgpMZXQncyBiZWdpbiBleHBsb3JpbmcgdGhlIHJlc3VsdHMgb2YgdGhlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMgYnkgc3VtbWFyaXppbmcgdGhlIHJlc3VsdHMuCgogIC0gd2Ugd2lsbCB1c2UgYm90aCB0aGUgZGVmYXVsdCBzZXR0aW5nIGZvciBwLXZhbHVlIGFuZCBvdXIgb3duIGFkanVzdGVkIHAtdmFsdWUgb2YgMC4wNQoKYGBge3J9CiMgc3VtbWFyeSByZXN1bHRzIG9mIGRkcyAod2l0aCBhIHJlLWxldmVsaW5nIG9mIHdpbGRfdHlwZSBjb250cm9sIHZzLiBlYWNoIHRyZWF0bWVudCwgYW5kIGludGVyYWN0aW9uIGJldHdlZW4gdHJlYXRtZW50IGFuZCBnZW5vdHlwZSkgCnN1bW1hcnkocmVzX2RkcykKCiMgd2l0aCBhZGp1c3RlZCBwLXZhbHVlIG9mIDAuMDUKcmVzXzAuMDVfZGRzIDwtIHJlc3VsdHMoZGRzLCBhbHBoYSA9IDAuMDUpCnN1bW1hcnkocmVzXzAuMDVfZGRzKQoKYGBgCgpXZSBjYW4gYWxzbyB1c2UgREVTZXEyJ3MgY29udHJhc3QgZnVuY3Rpb24gdG8gY29tcGFyZSBzcGVjaWZpYyBmYWN0b3IgbGV2ZWxzIGFmdGVyIHJlLWxldmVsaW5nLiBUaGlzIGFsbG93cyB1cyB0byBtYWtlIGFsbCB0aGUgbmVjZXNzYXJ5IGNvbXBhcmlzb25zIHdlIG5lZWQgd2l0aG91dCBoYXZpbmcgdG8gcmUtcnVuIHRoZSBERVNlcSBmdW5jdGlvbiB3aXRoIGRpZmZlcmVudCBwYXJhbWV0ZXJzIGV2ZXJ5IHRpbWUuIAoKQmVsb3cgYXJlIHRoZSBjb21wYXJpc29uIHJlc3VsdHMgYmV0d2VlbiBib3RoIHRoZSBfX19fIGFuZCB0aGUgX19fXzoKClRoZSBzdHJ1Z2dsZSBoZXJlIGlzIHRoYXQgSSBuZWVkIHRvIGJlIGFibGUgdG8gYmUgbW9yZSBleGFjdCB3aXRoIG15IGNvbXBhcmlzb25zOyBob3cgY2FuIEkgY29tcGFyZSB3aWxkX3R5cGVfMCB0byBrbm9ja291dF9saW5lXzAgd2hlbiB0aGUgbWV0YS1kYXRhIGlzIGNvbnRhaW5lZCBpbiBkaWZmZXJlbnQgY29sdW1ucyAoc29tZXRoaW5nIHRoZSBjb250cmFzdCBmdW5jdGlvbiBkb2VzIG5vdCBhY2NvbW1vZGF0ZSBmb3IpLgoKICAtIFRoZSBhbnN3ZXIgaXMgdGhhdCBJIHdvdWxkIG5lZWQgdG8gbWFrZSBhIGNvbWJpbmVkIGFuZCBtb3JlIHNwZWNpZmljIGNvbHVtbiBpbiBjb2xEYXRhIApgYGB7ciwgZXZhbCA9IEZBTFNFfQojVVNFRlVMIEZPUiBSRUZFUkVOQ0UgT04gVVNJTkcgQkFTRSBDT05UUkFTVCgpIEZVTkNUSU9OIEJVVCBUSElTIENPREUgQ0hVTksgRE9FUyBOT1QgUlVOCgojIGNvbnRyYXN0IGRpZmZlcmVudCBmYWN0b3IgbGV2ZWxzIChpLmUuIGVhY2ggdHJlYXRtZW50IHZzLiBTVC0zNSwgQ29sLTAgdnMuIFNULTM1LCBldGMuKQpyZXNfMF84MCA8LSByZXN1bHRzKGRkcywgY29udHJhc3QgPSBjKCJ0cmVhdG1lbnQiLCAiMCIsICI4MCIpLCBhbHBoYSA9IDAuMDUpICMgcC12YWx1ZSBvZiAwLjA1OyB0aGlzIGFscGhhID0gMC4wNSBkb2VzIG5vdCB3b3JrIAojIGRvIHNocmlua2FnZSBhZnRlciBlYWNoIGluZGl2aWR1YWwgY29udHJhc3QgCgojIGNvbnZlcnQgREVTZXEyIHJlc3VsdHMgdG8gYSBkYXRhIGZyYW1lCnJlc18wXzgwX2RmIDwtIGFzLmRhdGEuZnJhbWUocmVzXzBfODApCgojIGFkZCB0aGUgZ2VuZSBJRHMgYXMgYSBjb2x1bW4gaW4gdGhlIGRhdGEgZnJhbWUKcmVzXzBfODBfZGYkR2VuZUlEIDwtIHJvd25hbWVzKHJlc18wXzgwX2RmKQoKIyByZW9yZGVyIGNvbHVtbnMgdG8gcGxhY2UgR2VuZUlEIGFzIHRoZSBmaXJzdCBjb2x1bW4KcmVzXzBfODBfZGYgPC0gcmVzXzBfODBfZGZbLCBjKCJHZW5lSUQiLCBjb2xuYW1lcyhyZXNfMF84MF9kZilbLW5jb2wocmVzXzBfODBfZGYpXSldCgojIHdyaXRlIG91dHB1dCB0byBhIGNzdiBmaWxlIAp3cml0ZS5jc3YocmVzXzBfODBfZGYsICIuLi9kYXRhL0RFU2VxMl9yZXN1bHRzXzBfdnNfODAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpICMgZmlsdGVyIGFuZCBzYXZlIG9ubHkgdGhvc2UgdmFsdWVzIGhhdmUgYXJlIDwgMC4wNSAoZ2V0IGNyZWF0aXZlIHdpdGggcGFkaiBpbiB0aGUgcmVzXzBfODBfZGYpCmBgYAoKIyA2KSBWaXN1YWxpemluZyB0aGUgUmVzdWx0cyAKVGhlIGZvbGxvd2luZyBhcmUgZGlmZmVyZW50IHdheXMgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cyBvZiBvdXIgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcy4KCiMjIFBDQSAoUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcykKKioxLzIxLzI1KioKCiMjIyBFeHRyYWN0IGFuZCBUcmFuc2Zvcm0gTm9ybWFsaXplZCBEYXRhIApNYWtlIHN1cmUgdG8gdXNlIHRoZSB0aGUgbm9ybWFsaXplZCBkYXRhIGNvdW50cyBhbmQgbm90IHRoZSBERVNlcSByZXN1bHQgb3V0cHV0ISAKYGBge3J9CiMgcGVyZm9ybSB2YXJpYW5jZSBzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbiAoVlNUKQp2c2QgPC0gdnN0KGRkcywgYmxpbmQgPSBGQUxTRSkKCiMgQWx0ZXJuYXRpdmVseSwgdXNlIHJsb2cgKGxvZyB0cmFuc2Zvcm1hdGlvbikKIyB2c2QgPC0gcmxvZyhkZHMsIGJsaW5kID0gRkFMU0UpCmBgYAoKIyMjIFBlcmZvcm0gUENBIApgYGB7cn0KIyBleHRyYWN0IHRoZSB0cmFuc2Zvcm1lZCBkYXRhCnRyYW5zZm9ybWVkX2NvdW50cyA8LSBhc3NheSh2c2QpCgojIHBlcmZvcm0gUENBIG9uIHRoZSB0cmFuc2Zvcm1lZCBjb3VudHMKcGNhIDwtIHByY29tcCh0KHRyYW5zZm9ybWVkX2NvdW50cykpCmBgYAoKIyMjIEV4dHJhY3QgUENBIFJlc3VsdHMgZm9yIFBsb3R0aW5nIApgYGB7cn0KIyBjcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggUENBIHJlc3VsdHMgYW5kIHNhbXBsZSBtZXRhZGF0YQpwY2FfZGF0IDwtIGFzLmRhdGEuZnJhbWUocGNhJHgpICAjIFBDQSBzY29yZXMgZm9yIGVhY2ggc2FtcGxlCnBjYV9kYXQkc2FtcGxlIDwtIHJvd25hbWVzKHBjYV9kYXQpICAjIHNhbXBsZSBuYW1lcwpwY2FfZGF0JHRyZWF0bWVudCA8LSBjb2xEYXRhKGRkcykkdHJlYXRtZW50ICAjIGdyb3VwaW5nIHZhcmlhYmxlICh0cmVhdG1lbnQgY29uZGl0aW9uKQpwY2FfZGF0JGdlbm90eXBlIDwtIGNvbERhdGEoZGRzKSRnZW5vdHlwZSAgIyBhZGQgZ2Vub3R5cGUgdG8gdGhlIFBDQSBkYXRhCmBgYAoKIyMjIENyZWF0ZSBQQ0EgUGxvdCAKYGBge3J9CiMgcGxvdCB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzCmdncGxvdChwY2FfZGF0LCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSB0cmVhdG1lbnQsIHNoYXBlID0gZ2Vub3R5cGUsIGxhYmVsID0gc2FtcGxlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZW9tX3RleHQodmp1c3QgPSAtMSwgaGp1c3QgPSAwLjUsIHNpemUgPSAzKSArCiAgbGFicygKICAgIHRpdGxlID0gIlBDQSBQbG90IG9mIEFyYWJpZG9wc2lzIFNlbGVuaXVtIFRyZWF0bWVudDpcblN1bGZ1ciBUcmFuc3BvcnQgS25vY2tvdXQtTGluZSBhbmQgV2lsZC1UeXBlIFNhbXBsZXMiLAogICAgeCA9IHBhc3RlMCgiUEMxOiAiLCByb3VuZChzdW1tYXJ5KHBjYSkkaW1wb3J0YW5jZVsyLCAxXSAqIDEwMCwgMSksICIlIFZhcmlhbmNlIiksCiAgICB5ID0gcGFzdGUwKCJQQzI6ICIsIHJvdW5kKHN1bW1hcnkocGNhKSRpbXBvcnRhbmNlWzIsIDJdICogMTAwLCAxKSwgIiUgVmFyaWFuY2UiKSwKICAgIGNvbG9yID0gIlRyZWF0bWVudCIsCiAgICBzaGFwZSA9ICJHZW5vdHlwZSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyBNQSBQbG90IAoqKjEvMjgvMjUqKgoKIyMjIEdlbm90eXBlIFNocmlua2FnZSAKYGBge3J9CiMgU3RlcCAxOiBDb21wdXRlIHJlc3VsdHMgZm9yIHRoZSBjb250cmFzdApyZXMgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0ID0gYygiZ2Vub3R5cGUiLCAia25vY2tvdXRfbGluZSIsICJ3aWxkX3R5cGUiKSkKCiMgU3RlcCAyOiBBcHBseSBzaHJpbmthZ2UgdG8gdGhlIHJlc3VsdHMKcmVzX3NocmluayA8LSBsZmNTaHJpbmsoZGRzLCBjb2VmID0gImdlbm90eXBlX2tub2Nrb3V0X2xpbmVfdnNfd2lsZF90eXBlIiwgdHlwZSA9ICJhcGVnbG0iKQoKIyBjaGVjayByZXN1bHRzCnN1bW1hcnkocmVzX3NocmluaykKYGBgCgojIyMgVXBkYXRlIHRoZSBQQ0EgRGF0YSBGcmFtZQpgYGB7cn0KIyBjcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggdGhlIHJlc3VsdHMgZnJvbSBsZmNTaHJpbmsKcGNhX2RhdF9zaHJpbmsgPC0gYXMuZGF0YS5mcmFtZShyZXNfc2hyaW5rKQpwY2FfZGF0X3NocmluayRzaWduaWZpY2FuY2UgPC0gaWZlbHNlKHBjYV9kYXRfc2hyaW5rJHBhZGogPCAwLjA1LCAiU2lnbmlmaWNhbnQiLCAiTm90IFNpZ25pZmljYW50IikKCmBgYAoKIyMjIENyZWF0ZSBNQSBQbG90IApgYGB7cn0KIyBjcmVhdGUgTUEgcGxvdCB1c2luZyBnZ3Bsb3QyCmdncGxvdChwY2FfZGF0X3NocmluaywgYWVzKHggPSBiYXNlTWVhbiwgeSA9IGxvZzJGb2xkQ2hhbmdlLCBjb2xvciA9IHNpZ25pZmljYW5jZSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlNpZ25pZmljYW50IiA9ICJibHVlIiwgIk5vdCBTaWduaWZpY2FudCIgPSAiYmxhY2siKSkgKwogIHNjYWxlX3hfbG9nMTAoKSArICAjIEFwcGx5IGxvZyBzY2FsZSB0byB4LWF4aXMgKGJhc2VNZWFuKQogIGxhYnModGl0bGUgPSAiTUEgUGxvdDogRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gd2l0aCBTaHJpbmthZ2UiLAogICAgICAgeCA9ICJNZWFuIG9mIE5vcm1hbGl6ZWQgQ291bnRzIChsb2cgc2NhbGUpIiwgCiAgICAgICB5ID0gIlNocnVua2VuIExvZzIgRm9sZCBDaGFuZ2UiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCiMgNykgQW5hbHlzZXMgZm9yIEFsbCBUcmVhdG1lbnQgQ29tYmluYXRpb25zCioqMi8zLzI1IC0gMi80LzI1KioKCiMjIFN1YnNldHRpbmcgJ2NvbERhdGEnIGFuZCAnZGF0YV9jb3VudHMnIGludG8gSW5kaXZpZHVhbCBEYXRhIEZyYW1lcyAKSSB3aWxsIGJlIHN1YnNldHRpbmcgb3VyIHByb3Blcmx5IHN0cnVjdHVyZWQgJ2NvbERhdGEnIGRhdGEgZnJhbWUgcHJvZHVjZWQgZWFybGllciBpbiB0aGlzIHNjcmlwdCAoZm91bmQgdW5kZXIgIjEuIFByZXBhcmluZyBDb3VudCBEYXRhIikgYXMgd2VsbCBhcyB0aGUgJ2RhdGFfY291bnRzJyBkYXRhIGZyYW1lIHRvIG1hdGNoIGNvbHVtbiBhbmQgcm93bmFtZXMuIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBwcm9wZXJseSB1dGlsaXplIHRoZSBjb250cmFzdCgpIGZ1bmN0aW9uIERFU2VxMiBvZmZlcnMsIGdpdmluZyB1cyB0aGUgYWJpbGl0eSB0byBtb3JlIGR5bmFtaWNhbGx5IGNvbXBhcmUgb3VyIHNhbXBsZXMgYnkgdHJlYXRpbmcgdGhlIHZhcnlpbmcgY29sdW1ucyBhcyBpbmRlcGVuZGVudCBmYWN0b3JzLiAKCkFzIGEgcmVtaW5kZXIsIHdlIHdpbGwgYmUgbWFraW5nIG1hbnkgY29tcGFyaXNvbnMsIG9mIHdoaWNoIGFyZSBhcyBmb2xsb3dzOgoKICAtIGtub2Nrb3V0X2xpbmUgdHJlYXRtZW50IHZzLiB3aWxkX3R5cGUgdHJlYXRtZW50IAogICAgLSBLT18yMCB2cy4gV1RfMjAsIEtPXzQwIHZzLiBXVF80MCwgZXRjLgoKICAtIHdpbGRfdHlwZSBjb250cm9sIHZzLiBrbm9ja291dF9saW5lIGNvbnRyb2wKICAgIC0gV1RfMCB2cy4gS09fMAogIAogIC0gd2lsZF90eXBlIHRyZWF0bWVudCB2cy4gd2lsZF90eXBlIGNvbnRyb2wgCiAgICAtIFdUXzIwIHZzLiBXVF8wLCBXVF80MCB2cy4gV1RfMCwgZXRjLgogIAogIC0ga25vY2tvdXRfbGluZSB0cmVhdG1lbnQgdnMuIGtub2Nrb3V0X2xpbmUgY29udHJvbCAKICAgIC0gS09fMjAgdnMuIEtPXzAsIEtPXzQwIHZzLiBLT18wLCBldGMuCiAgICAKV2Ugd2lsbCBiZSB1c2luZyBhIHRvdGFsIG9mIDQgY29sRGF0YSBhbmQgZGF0YV9jb3VudHMgdmFyaWFudHMgKGluY2x1ZGluZyB0aGUgb3JpZ2luYWwgY29sRGF0YSBhbmQgZGF0YV9jb3VudHMgY3JlYXRlZCBlYXJsaWVyIGluIHRoaXMgc2NyaXB0KSB0aGF0IHdpbGwgdXRpbGl6ZSBlaXRoZXIgdGhlICd0cmVhdG1lbnQnIG9yICdncm91cCcgY29sdW1uIGFzIHRoZWlyIGNvbnRyYXN0IGFyZ3VtZW50cyBkZXBlbmRpbmcgb24gdGhlIHR5cGUgb2Ygc3BlY2lmaWNpdHkgb3VyIGNvbERhdGEgYWxsb3dzLiAKCiMjIyBPcmlnaW5hbCBjb2xEYXRhIGFuZCBkYXRhX2NvdW50cyAoZm9yIEtPIFRyZWF0bWVudCB2cy4gV1QgVHJlYXRtZW50KQpjb2xEYXRhOgpgYGB7ciwgZWNobyA9IEZBTFNFfQojIGRpc3BsYXkgb3JpZ2luYWwgY29sRGF0YSBwcm9kdWNlZCB1bmRlciAiMS4gUHJlcGFyaW5nIENvdW50IERhdGEiCmhlYWQoY29sRGF0YSkKCmBgYAoKZGF0YV9jb3VudHM6CmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgZGlzcGxheSBvcmlnaW5hbCBkYXRhX2NvdW50cyBwcm9kdWNlZCB1bmRlciAiMS4gUHJlcGFyaW5nIENvdW50IERhdGEiCmhlYWQoZGF0YV9jb3VudHMpCgpgYGAKCkRpc3BsYXkgZG9lcyBub3Qgb2J2aW91c2x5IHNob3cgdGhhdCBhbGwgcm93cy9jb2x1bW5zIGFyZSBpbmNsdWRlZC4KCiMjIyBPbmx5IENvbnRyb2xzIApjb2xEYXRhX2NvbnRyb2xzOgpgYGB7ciwgZWNobyA9IEZBTFNFfQojIHN1YnNldCBjb2xEYXRhIHRvIG9ubHkgaW5jbHVkZSByb3dzIHdoZXJlIHRyZWF0bWVudCBpcyAwIChjb250cm9sIGdyb3VwcykKY29sRGF0YV9jb250cm9scyA8LSBjb2xEYXRhW2NvbERhdGEkdHJlYXRtZW50ID09IDAsIF0KCmhlYWQoY29sRGF0YV9jb250cm9scykKCmBgYAoKZGF0YV9jb3VudHNfY29udHJvbHM6CmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgc3Vic2V0IGRhdGFfY291bnRzIHRvIG9ubHkgaW5jbHVkZSBjb2x1bW5zIHRoYXQgc3RhcnQgd2l0aCAnQ29sMDFfMScgb3IgJ1NUMzVfMScKY29udHJvbF9jb2x1bW5zIDwtIGdyZXAoIl4oQ29sMDFfMS4qfFNUMzVfMS4qKSIsIGNvbG5hbWVzKGRhdGFfY291bnRzKSkKCiMgc3Vic2V0IHRoZSBkYXRhX2NvdW50cyB0byBvbmx5IGluY2x1ZGUgdGhlIGFib3ZlIGNvbHVtbnMKZGF0YV9jb3VudHNfY29udHJvbHMgPC0gZGF0YV9jb3VudHNbLCBjb250cm9sX2NvbHVtbnNdCgpoZWFkKGRhdGFfY291bnRzX2NvbnRyb2xzKQoKYGBgCgojIyMgT25seSBXaWxkLVR5cGUgU2FtcGxlcyAKY29sRGF0YV93dDogCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgc3Vic2V0IGNvbERhdGEgdG8gb25seSBpbmNsdWRlIHJvd3Mgd2hlcmUgZ2Vub3R5cGUgaXMgd2lsZF90eXBlIApjb2xEYXRhX3d0IDwtIGNvbERhdGFbY29sRGF0YSRnZW5vdHlwZSA9PSAid2lsZF90eXBlIiwgXQoKaGVhZChjb2xEYXRhX3d0KQoKYGBgCgpkYXRhX2NvdW50c193dDoKYGBge3IsIGVjaG8gPSBGQUxTRX0KIyBzdWJzZXQgZGF0YV9jb3VudHMgdG8gb25seSBpbmNsdWRlIGNvbHVtbnMgdGhhdCBzdGFydCB3aXRoICdDb2wnCnd0X2NvbHVtbnMgPC0gZ3JlcCgiXihDb2wuKikiLCBjb2xuYW1lcyhkYXRhX2NvdW50cykpCgojIHN1YnNldCB0aGUgZGF0YV9jb3VudHMgdG8gb25seSBpbmNsdWRlIHRoZSBhYm92ZSBjb2x1bW5zCmRhdGFfY291bnRzX3d0IDwtIGRhdGFfY291bnRzWywgd3RfY29sdW1uc10KCmhlYWQoZGF0YV9jb3VudHNfd3QpCgpgYGAKCiMjIyBPbmx5IEtub2Nrb3V0LUxpbmUgU2FtcGxlcyAKY29sRGF0YV9rbzoKYGBge3IsIGVjaG8gPSBGQUxTRX0KIyBzdWJzZXQgY29sRGF0YSB0byBvbmx5IGluY2x1ZGUgcm93cyB3aGVyZSBnZW5vdHlwZSBpcyBrbm9ja291dF9saW5lIApjb2xEYXRhX2tvIDwtIGNvbERhdGFbY29sRGF0YSRnZW5vdHlwZSA9PSAia25vY2tvdXRfbGluZSIsIF0KCmhlYWQoY29sRGF0YV9rbykKCmBgYAoKZGF0YV9jb3VudHNfa286CmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgc3Vic2V0IGRhdGFfY291bnRzIHRvIG9ubHkgaW5jbHVkZSBjb2x1bW5zIHRoYXQgc3RhcnQgd2l0aCAnU1QzNScKa29fY29sdW1ucyA8LSBncmVwKCJeKFNUMzUuKikiLCBjb2xuYW1lcyhkYXRhX2NvdW50cykpCgojIHN1YnNldCB0aGUgZGF0YV9jb3VudHMgdG8gb25seSBpbmNsdWRlIHRoZSBhYm92ZSBjb2x1bW5zCmRhdGFfY291bnRzX2tvIDwtIGRhdGFfY291bnRzWywga29fY29sdW1uc10KCmhlYWQoZGF0YV9jb3VudHNfa28pCgpgYGAKCiMjIEZ1bmN0aW9uIGZvciBDcmVhdGluZyBWYXJ5aW5nICdkZHMnIE9iamVjdHMgCkkndmUgd3JpdHRlbiBhIGZ1bmN0aW9uIHRoYXQgYWxsb3dzIGZvciBlYXN5IG1hbmlwdWxhdGlvbiBhbmQgcHJvZHVjdGlvbiBvZiAnZGRzJyBvYmplY3RzIHVzaW5nIHRoZSBERVNlcURhdGFTZXRGcm9tTWF0cml4KCkgZnVuY3Rpb24uIEl0IHNlZW1zIHZlcnkgc2ltaWxhciB0byBzaW1wbHkgdXNpbmcgdGhlIERFU2VxMiBmdW5jdGlvbiBpdHNlbGYsIGhvd2V2ZXIsIHRoaXMgYWxsb3dzIGZvciBjbGVhbmVyIHByb2R1Y3Rpb24gb2YgdGhlIGRkcyBvYmplY3RzIGFuZCBpbmNsdWRlcyBjaG9zZW4gZmlsdGVyaW5nIG9mIGdlbmUgY291bnRzLiBJdCBhbHNvIGluY2x1ZGVzIGVycm9yIG1lc3NhZ2VzIHRvIGdyYWNlZnVsbHkgaGFsdCBpbXByb3BlciBpbnB1dHMuIAoKICAtIHJlYWRfY291bnRzID0gdGhlIGNvdW50RGF0YSBpbnB1dCAoYXNzdW1lZCBpdCBpcyBwcm9wZXJseSBzdHJ1Y3R1cmVkKQogIC0gY29sdW1uX2RhdGEgPSB0aGUgY29sRGF0YSBtZXRhIGRhdGEgdGhhdCBhbHNvIGNvbnRhaW5zIHRoZSBjb2x1bW5zIHVzZWQgZm9yIHRoZSBkZXNpZ24gb2YgdGhlIGRkcyBvYmplY3QKICAtIGRlc2lnbl9wYXJhbWV0ZXIgPSB5b3VyIGNob3NlbiBkZXNpZ24gZm9ybXVsYSB0aGF0IGFsaWducyB3aXRoIHRoZSBjb2xEYXRhIGNvbHVtbiBuYW1lcyAKICAtIGZpbHRlcl9sb3dfY291bnRzID0gYSBjaG9zZW4gbGltaXQgdG8gZmlsdGVyIG91dCBnZW5lIGNvdW50czsgZGVmYXVsdCBpcyBzZXQgdG8gbm8gZmlsdGVyaW5nIGlmIG5vdCBleHBsaWNpdGx5IHN0YXRlZCAKICAKVGhpcyBmdW5jdGlvbiBvbmx5IHdvcmtzIGlmIHRoZSBERVNlcTIgcGFja2FnZSBpcyBpbnN0YWxsZWQgYW5kIHJ1bm5pbmcgb24gdGhlIHNhbWUgc2NyaXB0LiAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmRkc19nZW5lcmF0b3IgPC0gZnVuY3Rpb24ocmVhZF9jb3VudHMsIGNvbHVtbl9kYXRhLCBkZXNpZ25fcGFyYW1ldGVyLCBmaWx0ZXJfbG93X2NvdW50cyA9IE5VTEwpIHsKICBpZiAoIWlzLmNoYXJhY3RlcihkZXNpZ25fcGFyYW1ldGVyKSkgewogICAgc3RvcCgiJ2Rlc2lnbl9wYXJhbWV0ZXInIG11c3QgYmUgYSBjaGFyYWN0ZXIgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgZGVzaWduIGZvcm11bGEuIikKICB9CiAgCiAgIyB2YWxpZGF0ZSBkZXNpZ24gdmFyaWFibGVzIGV4aXN0IGluIGNvbHVtbl9kYXRhCiAgZGVzaWduX3ZhcnMgPC0gdW5saXN0KHN0cnNwbGl0KGRlc2lnbl9wYXJhbWV0ZXIsICIgXFwrIHwgXFwqICIpKQogIG1pc3NpbmdfdmFycyA8LSBzZXRkaWZmKGRlc2lnbl92YXJzLCBjb2xuYW1lcyhjb2x1bW5fZGF0YSkpCiAgaWYgKGxlbmd0aChtaXNzaW5nX3ZhcnMpID4gMCkgewogICAgc3RvcCgiVGhlIGZvbGxvd2luZyBkZXNpZ24gdmFyaWFibGVzIGFyZSBtaXNzaW5nIGluICdjb2x1bW5fZGF0YSc6ICIsIHBhc3RlKG1pc3NpbmdfdmFycywgY29sbGFwc2UgPSAiLCAiKSkKICB9CiAgCiAgIyBjb252ZXJ0IGRlc2lnbiBwYXJhbWV0ZXIgaW5wdXQgaW50byBmb3JtdWxhCiAgZGVzaWduX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgifiIsIGRlc2lnbl9wYXJhbWV0ZXIpKQogIAogICMgY3JlYXRpbmcgREVTZXEyIG9iamVjdAogIGRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IHJlYWRfY291bnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2x1bW5fZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBkZXNpZ25fZm9ybXVsYSkKICAKICAjIHJlbW92ZSBsb3cgZ2VuZSBjb3VudHMgb25seSBpZiBhIGZpbHRlcmluZyB0aHJlc2hvbGQgaXMgc3BlY2lmaWVkCiAgaWYgKCFpcy5udWxsKGZpbHRlcl9sb3dfY291bnRzKSAmJiBpcy5udW1lcmljKGZpbHRlcl9sb3dfY291bnRzKSAmJiBmaWx0ZXJfbG93X2NvdW50cyA+IDApIHsKICAgIGtlZXBfY291bnRzIDwtIHJvd1N1bXMoY291bnRzKGRkcykpID49IGZpbHRlcl9sb3dfY291bnRzCiAgICBkZHMgPC0gZGRzW2tlZXBfY291bnRzLF0KICB9CiAgCiAgcmV0dXJuKGRkcykKfQoKYGBgCgojIyBHZW5lcmF0aW5nIGFuZCBSZS1sZXZlbGluZyAnZGRzJyBPYmplY3RzIApVc2luZyBhbGwgb2YgdGhlIHBhcnRpY3VsYXIgY29sRGF0YSBzdWItc2V0cywgSSB3aWxsIGJlIHByb2R1Y2luZyB2YXJ5aW5nICdkZHMnIG9iamVjdHMgdGhhdCB3aWxsIGhhdmUgZGlmZmVyZW50IGRlc2lnbiBmb3JtdWxhcyBkZXBlbmRpbmcgb24gaG93IHNwZWNpZmljIHdlIG5lZWQgdGhlIGNvbXBhcmlzb25zIGJldHdlZW4gc2FtcGxlcyB0byBiZS4gVGhlc2Ugb2JqZWN0cyB3aWxsIHRoZW4gYmUgaW1wbGVtZW50ZWQgd2l0aCB0aGUgY29udHJhc3QgZnVuY3Rpb24gZm9yIG1vcmUgc3BlY2lmaWMgY29tcGFyaXNvbnMgd2l0aGluIGVhY2ggc3Vic2V0LiAKCiAgLSBFYWNoICdkZHMnIG9iamVjdCB3aWxsIGJlIHByb2R1Y2VkIGFuZCBmaWx0ZXJlZCB1c2luZyB0aGUgZGRzX2dlbmVyYXRvcigpIGZ1bmN0aW9uIAoKQWxsICdkZHMnIG9iamVjdHMgd2lsbCBiZSBmYWN0b3IgcmUtbGV2ZWxlZCB0byByZWZlcmVuY2UgdGhlIGJhc2VsaW5lIHdoaWNoIGlzIHRoZSB3aWxkLXR5cGUgY29udHJvbCBzYW1wbGUgKHRyZWF0bWVudCA9IDAgYW5kIGdlbm90eXBlID0gd2lsZF90eXBlIG9yIGdyb3VwID0gd2lsZF90eXBlXzApLiBUaGlzIHdpbGwgYmUgdGhlIHNhbWUgZm9yIGV2ZXJ5IGFuYWx5c2VzLiAKCiMjIyBLTyBUcmVhdG1lbnQgdnMuIFdUIFRyZWF0bWVudCAKYGBge3J9CmRkc190cmVhdG1lbnQgPC0gZGRzX2dlbmVyYXRvcihkYXRhX2NvdW50cywgY29sRGF0YSwgImdyb3VwIiwgMTApCgojIHNldCByZWZlcmVuY2UgbGV2ZWw6IGdyb3VwIHRvIHd0XzAKZGRzX3RyZWF0bWVudCRncm91cCA8LSByZWxldmVsKGRkc190cmVhdG1lbnQkZ3JvdXAsIHJlZiA9ICJ3dF8wIikKCmRkc190cmVhdG1lbnQKYGBgCgojIyMgT25seSBDb250cm9scyAKYGBge3J9CmRkc19jb250cm9scyA8LSBkZHNfZ2VuZXJhdG9yKGRhdGFfY291bnRzX2NvbnRyb2xzLCBjb2xEYXRhX2NvbnRyb2xzLCAiZ2Vub3R5cGUiLCAxMCkKCiMgc2V0IHJlZmVyZW5jZSBsZXZlbDogZ2Vub3R5cGUgdG8gd2lsZF90eXBlIApkZHNfY29udHJvbHMkZ2Vub3R5cGUgPC0gcmVsZXZlbChkZHNfY29udHJvbHMkZ2Vub3R5cGUsIHJlZiA9ICJ3aWxkX3R5cGUiKQoKZGRzX2NvbnRyb2xzCmBgYAoKIyMjIE9ubHkgV2lsZC1UeXBlIFNhbXBsZXMgCmBgYHtyfQpkZHNfd3QgPC0gZGRzX2dlbmVyYXRvcihkYXRhX2NvdW50c193dCwgY29sRGF0YV93dCwgInRyZWF0bWVudCIsIDEwKQoKIyBzZXQgcmVmZXJlbmNlIGxldmVsOiB0cmVhdG1lbnQgdG8gMApkZHNfd3QkdHJlYXRtZW50IDwtIHJlbGV2ZWwoZGRzX3d0JHRyZWF0bWVudCwgcmVmID0gIjAiKQoKZGRzX3d0CmBgYAoKIyMjIE9ubHkgS25vY2tvdXQtTGluZSBTYW1wbGVzCmBgYHtyfQpkZHNfa28gPC0gZGRzX2dlbmVyYXRvcihkYXRhX2NvdW50c19rbywgY29sRGF0YV9rbywgInRyZWF0bWVudCIsIDEwKQoKIyBzZXQgcmVmZXJlbmNlIGxldmVsOiB0cmVhdG1lbnQgdG8gMApkZHNfa28kdHJlYXRtZW50IDwtIHJlbGV2ZWwoZGRzX2tvJHRyZWF0bWVudCwgcmVmID0gIjAiKQoKZGRzX2tvCmBgYAoKIyMgRnVuY3Rpb25zIGZvciBXcml0aW5nIFNocnVua2VuIENvbXBhcmlzb24gRGF0YSB0byBDU1YncwpUbyBhbGxvdyBmb3IgZWFzaWVyIHJlcHJvZHVjaWJpbGl0eSBhbmQgbGVzcyByZWR1bmRhbmN5IGluIHRoZSBjb2RlLCBJJ3ZlIHdyaXR0ZW4gdHdvIGZ1bmN0aW9ucyB0aGF0IGFsbG93IGZvciBwcm9kdWNpbmcgZHluYW1pYyBjb250cmFzdHMgdGhhdCB1dGlsaXplIGVpdGhlciBhcGVnbG0gc2hyaW5rYWdlIHdpdGggY29lZmZpY2llbnRzIG9yIGFzaHIgc2hyaW5rYWdlIHdpdGggY29udHJhc3RzLCBhbmQgd3JpdGVzIHRoZSBvdXRwdXQgdG8gYSBkeW5hbWljYWxseSBuYW1lZCBDU1YgZmlsZS4KCiMjIyBjb2VmX3Nocmlua2FnZV9jb250cmFzdCgpIEZ1bmN0aW9uIApBcyBtZW50aW9uZWQsIHRoZSBjb2VmX3Nocmlua2FnZV9jb250cmFzdCgpIGZ1bmN0aW9uIHVzaW5nIGFwZWdsbSBzaHJpbmthZ2UgYW5kIGNvZWZmaWNpZW50cyBmcm9tIHRoZSAnZGRzJyBsZXZlbHMuIFRoaXMgaXMgZXNwZWNpYWxseSB1c2VmdWwgdG8gbWFpbnRhaW4gaW5kZXBkZW5jZSBhbW9uZ3N0IGNvbXBhcmlzb25zIGFuZCB0aGUgZmFjdG9yIGxldmVscy4gCgogIC0gZGRzX29iamVjdCA9IHRoZSBzcGVjaWZpYyAnZGRzJyBvYmplY3QgdXNlZCBmb3IgdGhlIGNvbnRyYXN0IAogIC0gY29sdW1uID0gdGhlIHNwZWNpZmljIGNvbHVtbiB1c2VkIHRvIHJlZmVyZW5jZSB0aGUgZmFjdG9yIGxldmVscyBiZWluZyBjb250cmFzdGVkIAogIC0gbGV2ZWxfMSA9IHRoZSBmaXJzdCBmYWN0b3IgbGV2ZWwgYmVpbmcgY29tcGFyZWQgCiAgLSBwcmlvcml0eV9sZXZlbF8yID0gdGhlIHNlY29uZCBmYWN0b3IgbGV2ZWwgYmVpbmcgY29tcGFyZWQgKGdlbmVyYWxseSB0aGUgcHJlZmVycmVkIGJhc2VsaW5lIGFjY29yZGluZyB0byByZWZlcmVuY2UgbGV2ZWwpCiAgLSBvdXRwdXRfZGlyID0gdGhlIHJlbGF0aXZlIHBhdGggdG8gd2hpY2ggeW91IHdhbnQgeW91ciBkeW5hbWljYWxseSBuYW1lZCBDU1YgZmlsZSB0byBnbyAodGhlIGRlZmF1bHQgaXMgc2V0IGZvciBteSBwZXJzb25hbCBjb21wdXRlcjsKICBhZGp1c3QgaXQgdG8geW91ciBuZWVkcykKICAKVGhpcyBmdW5jdGlvbiBhbHNvIGR5bmFtaWNhbGx5IGluY29ycG9yYXRlcyB0aGUgcHJvcGVyIGNvZWZmaWNpZW50IHVzaW5nIHRoZSBjb2x1bW4sIGxldmVsXzEsIGFuZCBwcmlvcml0eV9sZXZlbF8yIHBhcmFtZXRlcnMuIEl0IGFsc28gaGFzIGEgYnVpbHQtaW4gY2hlY2sgdG8gbWFrZSBzdXJlIHRoZSBjb2VmZmljaWVudCBleGlzdHMgaW4gc2FpZCBkZHMgb2JqZWN0LgogIApUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIHRoZSBhcGVnbG0gcGFja2FnZSBpbnN0YWxsZWQgYW5kIHJ1bm5pbmcgb24gdGhlIHNhbWUgc2NyaXB0IHRvIHdvcmsuIAoKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0IDwtIGZ1bmN0aW9uKGRkc19vYmplY3QsIGNvbHVtbiwgbGV2ZWxfMSwgcHJpb3JpdHlfbGV2ZWxfMiwgb3V0cHV0X2RpciA9ICIuLi9kYXRhL2FyYWJpZG9wc2lzX2Rlc2VxMl9yZXN1bHRzLyIpIHsKICAjIGNvbnN0cnVjdCB0aGUgZXhwZWN0ZWQgY29lZmZpY2llbnQgbmFtZQogIGNvZWZfbmFtZSA8LSBwYXN0ZTAoY29sdW1uLCAiXyIsIGxldmVsXzEsICJfdnNfIiwgcHJpb3JpdHlfbGV2ZWxfMikKICAKICAjIGVuc3VyZSB0aGUgY29lZmZpY2llbnQgZXhpc3RzCiAgZGRzX25hbWUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGRkc19vYmplY3QpKSAgIyBncmFiIHRoZSBvYmplY3QncyBuYW1lIGFzIGEgc3RyaW5nIAogIGlmICghKGNvZWZfbmFtZSAlaW4lIHJlc3VsdHNOYW1lcyhkZHNfb2JqZWN0KSkpIHsKICAgIHN0b3AocGFzdGUoIkNvZWZmaWNpZW50IiwgY29lZl9uYW1lLCAibm90IGZvdW5kIGluIG9iamVjdCIsIGRkc19uYW1lLCAiLiBDaGVjayByZXN1bHRzTmFtZXMoIiwgZGRzX25hbWUsICIpLiIpKQogICAgfQoKICAjIGFwcGx5IHNocmlua2FnZSB1c2luZyBhcGVnbG0KICBzaHJpbmtfcmVzdWx0IDwtIGxmY1NocmluayhkZHNfb2JqZWN0LCBjb2VmID0gY29lZl9uYW1lLCB0eXBlID0gImFwZWdsbSIpCgogICMgY29udmVydCB0aGUgc2hyaW5rYWdlIHJlc3VsdHMgdG8gYSBkYXRhIGZyYW1lCiAgcmVzdWx0X2RmIDwtIGFzLmRhdGEuZnJhbWUoc2hyaW5rX3Jlc3VsdCkKCiAgIyBhZGQgdGhlIGdlbmUgSURzIGFzIGEgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lCiAgcmVzdWx0X2RmJEdlbmVJRCA8LSByb3duYW1lcyhyZXN1bHRfZGYpCgogICMgcmUtb3JkZXIgY29sdW1ucyB0byBwbGFjZSBHZW5lSUQgYXMgdGhlIGZpcnN0IGNvbHVtbgogIHJlc3VsdF9kZiA8LSByZXN1bHRfZGZbLCBjKCJHZW5lSUQiLCBjb2xuYW1lcyhyZXN1bHRfZGYpWy1uY29sKHJlc3VsdF9kZildKV0KCiAgIyBnZXQgdGhlIG5hbWUgb2YgdGhlIGRkc19vYmplY3QgKHVzZWQgZm9yIG91dHB1dF9maWxlIG5hbWUpCiAgZGRzX25hbWUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGRkc19vYmplY3QpKQoKICAjIGdlbmVyYXRlIGR5bmFtaWMgb3V0cHV0IGZpbGUgbmFtZQogIG91dHB1dF9maWxlIDwtIHBhc3RlMChvdXRwdXRfZGlyLCAiZGVzZXEyXyIsIGRkc19uYW1lLCAiXyIsIGxldmVsXzEsICJfdnNfIiwgcHJpb3JpdHlfbGV2ZWxfMiwgIi5jc3YiKQoKICAjIHdyaXRlIG91dHB1dCB0byBhIENTViBmaWxlCiAgd3JpdGUuY3N2KHJlc3VsdF9kZiwgb3V0cHV0X2ZpbGUsIHJvdy5uYW1lcyA9IEZBTFNFKQp9CgpgYGAKCiMjIyBhc2hyX3Nocmlua2FnZV9jb250cmFzdCgpIEZ1bmN0aW9uIApUaGUgZm9sbG93aW5nIGFzaHJfc2hyaW5rYWdlX2NvbnRyYXN0KCkgZnVuY3Rpb24gdXRpbGl6ZXMgdGhlIGFzaHIgc2hyaW5rYWdlIGVzdGltYXRvciBhbG9uZyB3aXRoIHRoZSBjb250cmFzdCBmdW5jdGlvbiB0byBhbGxvdyBtdXRhYmlsaXR5IGluIGNvbnRyYXN0aW5nIGZhY3RvciBsZXZlbHMgdGhhdCBhcmUgd2VyZSBub3Qgb3JpZ2luYWxseSBzZXQgZHVyaW5nIGZhY3RvciByZS1sZXZlbGluZy4gSW4gb3RoZXIgd29yZHMsIHdlIGNhbiBjb21wYXJlIGRpZmZlcmVudCBmYWN0b3IgbGV2ZWxzIHRoYXQgYXJlIGRvIG5vdCBpbnZvbHZlIHRoZSByZWZlcmVuY2UgbGV2ZWwuIAoKQWxsIG9mIHRoZSBwYXJhbWV0ZXJzIHVzZWQgaW4gdGhlIGZ1bmN0aW9uIGFib3ZlIGFyZSB1c2VkIGFuZCByZXByZXNlbnRlZCB0aGUgc2FtZSBpbiB0aGlzIG9uZS4gVGhlIHNhbWUgY2hlY2tzIGFyZSBhbHNvIGluIHBsYWNlLgoKVGhpcyBmdW5jdGlvbiByZXF1aXJlcyB0aGUgYXNociBwYWNrYWdlIHRvIGJlIGluc3RhbGxlZCBhbmQgcnVubmluZyBvbiB0aGUgc2FtZSBzY3JpcHQuIApgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KYXNocl9zaHJpbmthZ2VfY29udHJhc3QgPC0gZnVuY3Rpb24oZGRzX29iamVjdCwgY29sdW1uLCBsZXZlbF8xLCBwcmlvcml0eV9sZXZlbF8yLCBvdXRwdXRfZGlyID0gIi4uL2RhdGEvYXJhYmlkb3BzaXNfZGVzZXEyX3Jlc3VsdHMvIikgewogICMgZW5zdXJlIHRoZSBjb250cmFzdCBsZXZlbHMgZXhpc3QKICBkZHNfbmFtZSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoZGRzX29iamVjdCkpICAjIGdyYWIgdGhlIG9iamVjdCdzIG5hbWUgYXMgYSBzdHJpbmcgCiAgZmFjdG9yX2xldmVscyA8LSBsZXZlbHMoY29sRGF0YShkZHNfb2JqZWN0KVtbY29sdW1uXV0pICAjIEdldCBsZXZlbHMgb2YgdGhlIHNwZWNpZmllZCBjb2x1bW4KICAKICAjIGNoZWNrIHdoZXRoZXIgdGhlIGxldmVscyBleGlzdCBpbiB0aGUgZmFjdG9yIGNvbHVtbgogIGlmICghKGxldmVsXzEgJWluJSBmYWN0b3JfbGV2ZWxzKSB8ICEocHJpb3JpdHlfbGV2ZWxfMiAlaW4lIGZhY3Rvcl9sZXZlbHMpKSB7CiAgICBzdG9wKHBhc3RlKCJMZXZlbHMiLCBsZXZlbF8xLCAib3IiLCBwcmlvcml0eV9sZXZlbF8yLCAibm90IGZvdW5kIGluIGNvbHVtbiIsIGNvbHVtbiwgIm9mIG9iamVjdCIsIGRkc19uYW1lLCAiLiBDaGVjayBsZXZlbHMoY29sRGF0YSgiLAogICAgICAgICAgICAgICBkZHNfbmFtZSwgIilbWyIsIGNvbHVtbiwgIl1dKS4iKSkKICAgIH0KICAKICAjIGFwcGx5IHNocmlua2FnZSB1c2luZyBhc2hyIGFuZCBjb250cmFzdAogIHNocmlua19yZXN1bHQgPC0gbGZjU2hyaW5rKGRkc19vYmplY3QsIGNvbnRyYXN0ID0gYyhjb2x1bW4sIGxldmVsXzEsIHByaW9yaXR5X2xldmVsXzIpLCB0eXBlID0gImFzaHIiKQoKICAjIGNvbnZlcnQgdGhlIHNocmlua2FnZSByZXN1bHRzIHRvIGEgZGF0YSBmcmFtZQogIHJlc3VsdF9kZiA8LSBhcy5kYXRhLmZyYW1lKHNocmlua19yZXN1bHQpCgogICMgYWRkIHRoZSBnZW5lIElEcyBhcyBhIGNvbHVtbiBpbiB0aGUgZGF0YSBmcmFtZQogIHJlc3VsdF9kZiRHZW5lSUQgPC0gcm93bmFtZXMocmVzdWx0X2RmKQoKICAjIHJlLW9yZGVyIGNvbHVtbnMgdG8gcGxhY2UgR2VuZUlEIGFzIHRoZSBmaXJzdCBjb2x1bW4KICByZXN1bHRfZGYgPC0gcmVzdWx0X2RmWywgYygiR2VuZUlEIiwgY29sbmFtZXMocmVzdWx0X2RmKVstbmNvbChyZXN1bHRfZGYpXSldCgogICMgZ2VuZXJhdGUgZHluYW1pYyBvdXRwdXQgZmlsZSBuYW1lCiAgb3V0cHV0X2ZpbGUgPC0gcGFzdGUwKG91dHB1dF9kaXIsICJkZXNlcTJfIiwgZGRzX25hbWUsICJfIiwgbGV2ZWxfMSwgIl92c18iLCBwcmlvcml0eV9sZXZlbF8yLCAiLmNzdiIpCgogICMgd3JpdGUgb3V0cHV0IHRvIGEgQ1NWIGZpbGUKICB3cml0ZS5jc3YocmVzdWx0X2RmLCBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpCn0KCmBgYAoKIyMgUnVuIFZhcnlpbmcgREVTZXEyIEFuYWx5c2VzIGFuZCBDb250cmFzdHMKQmVsb3cgd2lsbCBjb250YWluIHRoZSBjb2RlIHJ1bm5pbmcgYWxsIG9mIHRoZSB2YXJpb3VzIERFU2VxMiBhbmFseXNlcyB3ZSBuZWVkIGFsb25nIHdpdGggc3BlY2lmaWMgY29udHJhc3RzLiBFYWNoIGNvbnRyYXN0IHdpbGwgYmUgd3JpdHRlbiB0byBhIENTViBmaWxlIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBmdXJ0aGVyIGFuYWx5c2VzLiBUaGVyZSB3aWxsIGJlIG1hbnkgZmlsZXMgc2F2ZWQgdG8gYSBzdWJkaXJlY3Rvcnkgd2l0aGluIG15ICdkYXRhJyBkaXJlY3RvcnkuIAoKIyMjIEtPIFRyZWF0bWVudCB2cy4gV1QgVHJlYXRtZW50IApgYGB7cn0KIyBydW4gdGhlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMKZGRzX3RyZWF0bWVudCA8LSBERVNlcShkZHNfdHJlYXRtZW50KQoKIyB2aWV3IHdoYXQgY29uZGl0aW9ucyB3ZXJlIGNvbnRyYXN0ZWQgdG8gbWFrZSBzdXJlIHRoZXkgbWF0Y2ggd2hhdCB3ZSBhY3R1YWxseSBtZWFudCB0byBjb21wYXJlIApyZXN1bHRzTmFtZXMoZGRzX3RyZWF0bWVudCkKCiMgc3RvcmUgdGhlIHJlc3VsdHMgaW4gYW4gb2JqZWN0IApyZXNfZGRzX3RyZWF0bWVudCA8LSByZXN1bHRzKGRkc190cmVhdG1lbnQpCgojIHZpZXcgREVTZXEyIHJlc3VsdHMgCnJlc19kZHNfdHJlYXRtZW50CgojIHN1bW1hcnkgcmVzdWx0cyBvZiByZXNfZGRzX3RyZWF0bWVudCAod2l0aCByZS1sZXZlbCBvZiBncm91cCA9IHd0XzApCnN1bW1hcnkocmVzX2Rkc190cmVhdG1lbnQsIDAuMDUpCmBgYAoKV2Ugd2lsbCBiZSB1c2luZyB0aGUgYXNocl9zaHJpbmthZ2VfY29udHJhc3QoKSBmdW5jdGlvbiBmb3IgdGhlIGNvbXBhcmlzb25zIHJlZ2FyZGluZyBkZHNfdHJlYXRtZW50IGFzIHRoZXkgaGF2ZSBhIHJlZmVyZW5jZSBsZXZlbCBvZiB3dF8wIGJ1dCByZXF1aXJlIGNvbnRyYXN0cyBiZXR3ZWVuIHRyZWF0bWVudHMgb2YgYm90aCBnZW5vdHlwZXMgdG8gZWFjaCBvdGhlciwgbm90IHRvIHRoZSBjb250cm9scy4gSW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgYmUgY29tcGFyaW5nIHd0XzIwIHZzLiBrb18yMCwgd3RfNDAgdnMuIGtvXzQwLCBldGMuLCBpbiB0aGVzZSAnZGRzJyByZXN1bHRzLiBUaGVyZWZvcmUsIHdlIG5lZWQgdGhlIGFiaWxpdHkgdG8gYmUgc3BlY2lmaWMgYWJvdXQgb3VyIGNvbnRyYXN0cyAodXNpbmcgdGhlIGNvbnRyYXN0KCkgZnVuY3Rpb24pIGFuZCBzdGlsbCBiZSBhYmxlIHRvIHNocmluayB0aGUgZGF0YSB3aGljaCBpcyBub3QgZnVuY3Rpb25hbCB3aXRoIGFwZWdsbSwgYnV0IGlzIGZ1bmN0aW9uYWwgd2l0aCBhc2hyLgoKIyMjIyBXVF8yMCB2cy4gS09fMjAgCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQphc2hyX3Nocmlua2FnZV9jb250cmFzdChkZHNfdHJlYXRtZW50LCAiZ3JvdXAiLCAia29fMjAiLCAid3RfMjAiKQpgYGAKCiMjIyMgV1RfNDAgdnMuIEtPXzQwIApgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KYXNocl9zaHJpbmthZ2VfY29udHJhc3QoZGRzX3RyZWF0bWVudCwgImdyb3VwIiwgImtvXzQwIiwgInd0XzQwIikKYGBgCgojIyMjIFdUXzYwIHZzLiBLT182MCAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmFzaHJfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc190cmVhdG1lbnQsICJncm91cCIsICJrb182MCIsICJ3dF82MCIpCmBgYAoKIyMjIyBXVF84MCB2cy4gS09fODAgCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQphc2hyX3Nocmlua2FnZV9jb250cmFzdChkZHNfdHJlYXRtZW50LCAiZ3JvdXAiLCAia29fODAiLCAid3RfODAiKQpgYGAKCiMjIyBPbmx5IENvbnRyb2xzIApgYGB7cn0KIyBydW4gdGhlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMKZGRzX2NvbnRyb2xzIDwtIERFU2VxKGRkc19jb250cm9scykKCiMgdmlldyB3aGF0IGNvbmRpdGlvbnMgd2VyZSBjb250cmFzdGVkIHRvIG1ha2Ugc3VyZSB0aGV5IG1hdGNoIHdoYXQgd2UgYWN0dWFsbHkgbWVhbnQgdG8gY29tcGFyZSAKcmVzdWx0c05hbWVzKGRkc19jb250cm9scykKCiMgc3RvcmUgdGhlIHJlc3VsdHMgaW4gYW4gb2JqZWN0IApyZXNfZGRzX2NvbnRyb2xzIDwtIHJlc3VsdHMoZGRzX2NvbnRyb2xzKQoKIyB2aWV3IERFU2VxMiByZXN1bHRzIApyZXNfZGRzX2NvbnRyb2xzCgojIHN1bW1hcnkgcmVzdWx0cyBvZiByZXNfZGRzX2NvbnRyb2xzICh3aXRoIHJlLWxldmVsIG9mIGdlbm90eXBlID0gd2lsZF90eXBlKQpzdW1tYXJ5KHJlc19kZHNfY29udHJvbHMsIDAuMDUpCmBgYAoKRm9yIHRoZSBhbGwgb2YgdGhlIHJlbWFpbmluZyBjb21wYXJpc29ucywgd2Ugd2lsbCBiZSB1c2luZyB0aGUgY29lZl9zaHJpbmthZ2VfY29udHJhc3QoKSBmdW5jdGlvbiBhcyB0aGUgZGF0YSBmb3IgdGhlIGRkc19jb250cm9scywgZGRzX3d0LCBhbmQgZGRzX2tvIG9iamVjdHMgaGF2ZSBhbGwgYmVlbiBzdWJzZXQgYW5kIHJlLWxldmVsZWQgdG8gcmVmZXJlbmNlIHRoZSB3aWxkX3R5cGUgY29udHJvbCB2YXJpYW50IHdoZXJlIHBvc3NpYmxlIChkZHNfY29udHJvbHMgYW5kIGRkc193dCkgb3IgdGhlIGtub2Nrb3V0X2xpbmUgY29udHJvbCB2YXJpYW50IGluIGRkc19rby4gSW4gdGhlc2UgY2FzZXMsIHdlIGFyZSBjb21wYXJpbmcgYWxsIGluc3RhbmNlcyBzcGVjaWZpY2FsbHkgdG8gdGhlIHJlZmVyZW5jZSBsZXZlbCwgdGhlcmVmb3JlLCB0aGUgaW50ZXJjZXB0cyBhcmUgdmlhYmxlIGNvZWZmaWNpZW50cyB0byB1c2Ugd2l0aCB0aGUgYXBlZ2xtIHNocmlua2FnZSBlc3RpbWF0b3IuIEFsdGhvdWdoIHdlIGNhbm5vdCB1c2UgdGhlIGNvbnRyYXN0KCkgZnVuY3Rpb24gd2l0aCBhcGVnbG0uIAoKVGhlIHZpZ25ldHRlJ3Mgb24gREVTZXEyIGRlc2NyaWJlIGFwZWdsbSBhcyBhIGhpZ2hlciBwZXJmb3JtYW5jZSBlc3RpbWF0b3IgdGhhbiBhc2hyLiBGb3Igbm93LCBJIGFtIHVzaW5nIGEgbWl4IGZvciB0aGUgc2FrZSBvZiBlYXNlIGluIHJlcGVhdGVkIHByb2R1Y3Rpb24gb2YgdGhlIHJlc3VsdGluZyBkZHMgZGF0YSBzZXRzLiBEZXBlbmRpbmcgb24gb3VyIGZ1dHVyZSBuZWVkcywgd2UgY2FuIGFwcGx5IGVpdGhlciBmdW5jdGlvbiB0byBhbGwgdGhlIGRhdGEgdG8gbWFpbnRhaW4gY29uc2lzdGVuY3kuIAoKIyMjIyBXVF8wIHZzLiBLT18wIApJIGhvbmVzdGx5IHNob3VsZCBub3QgbmVlZCB0byBldmVuIGNvbnRyYXN0IHRoaXMgYXMgdGhlIG9ubHkgcm93cyBpbiB0aGUgJ2Rkc19jb250cm9scycgYXJlIHRoZSB0cmlwbGFjdGVzIG9mIHRoZSBjb250cm9sIGdyb3VwcyBmb3IgYm90aCB0aGUga25vY2tvdXRfbGluZSBhbmQgdGhlIHdpbGRfdHlwZSB2YXJpYW50cy4gSG93ZXZlciwgSSBoYXZlIHRoZSBydW5fc2hyaW5rYWdlX2NvbnRyYXN0KCkgZnVuY3Rpb24gYWxyZWFkeSBhbmQgaXQgc2VlbXMgdG8gd29yayBwcmV0dHkgd2VsbCwgYW5kIGluY29ycG9yYXRlcyBzaHJpbmthZ2Ugc28gd2Ugd2lsbCBqdXN0IHVzZSBpdC4gCgoqKldpbGwgY29udHJhc3RpbmcgaGF2ZSBhbnkgbmVnYXRpdmUgZWZmZWN0IG9uIHRoZSBvdXRjb21lIG9mIGFuIGFscmVhZHkgY29tcGxldGVkIGNvbXBhcmlzb24/KioKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc19jb250cm9scywgImdlbm90eXBlIiwgImtub2Nrb3V0X2xpbmUiLCAid2lsZF90eXBlIikKYGBgCgojIyMgT25seSBXaWxkLVR5cGUgU2FtcGxlcyAKYGBge3J9CiMgcnVuIHRoZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzCmRkc193dCA8LSBERVNlcShkZHNfd3QpCgojIHZpZXcgd2hhdCBjb25kaXRpb25zIHdlcmUgY29udHJhc3RlZCB0byBtYWtlIHN1cmUgdGhleSBtYXRjaCB3aGF0IHdlIGFjdHVhbGx5IG1lYW50IHRvIGNvbXBhcmUgCnJlc3VsdHNOYW1lcyhkZHNfd3QpCgojIHN0b3JlIHRoZSByZXN1bHRzIGluIGFuIG9iamVjdCAKcmVzX2Rkc193dCA8LSByZXN1bHRzKGRkc193dCkKCiMgdmlldyBERVNlcTIgcmVzdWx0cyAKcmVzX2Rkc193dAoKIyBzdW1tYXJ5IHJlc3VsdHMgb2YgcmVzX2Rkc193dCAod2l0aCByZS1sZXZlbCBvZiB0cmVhdG1lbnQgPSAwIGZvciB3aWxkX3R5cGUpCnN1bW1hcnkocmVzX2Rkc193dCwgMC4wNSkKYGBgCgojIyMjIFdUXzAgdnMuIFdUXzIwCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQpjb2VmX3Nocmlua2FnZV9jb250cmFzdChkZHNfd3QsICJ0cmVhdG1lbnQiLCAiMjAiLCAiMCIpCmBgYAoKIyMjIyBXVF8wIHZzLiBXVF80MCAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc193dCwgInRyZWF0bWVudCIsICI0MCIsICIwIikKYGBgCgojIyMjIFdUXzAgdnMuIFdUXzYwCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQpjb2VmX3Nocmlua2FnZV9jb250cmFzdChkZHNfd3QsICJ0cmVhdG1lbnQiLCAiNjAiLCAiMCIpCmBgYAoKIyMjIyBXVF8wIHZzLiBXVF84MCAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc193dCwgInRyZWF0bWVudCIsICI4MCIsICIwIikKYGBgCgojIyMgT25seSBLbm9ja291dC1MaW5lIFNhbXBsZXMKYGBge3J9CiMgcnVuIHRoZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzCmRkc19rbyA8LSBERVNlcShkZHNfa28pCgojIHZpZXcgd2hhdCBjb25kaXRpb25zIHdlcmUgY29udHJhc3RlZCB0byBtYWtlIHN1cmUgdGhleSBtYXRjaCB3aGF0IHdlIGFjdHVhbGx5IG1lYW50IHRvIGNvbXBhcmUgCnJlc3VsdHNOYW1lcyhkZHNfa28pCgojIHN0b3JlIHRoZSByZXN1bHRzIGluIGFuIG9iamVjdCAKcmVzX2Rkc19rbyA8LSByZXN1bHRzKGRkc19rbykKCiMgdmlldyBERVNlcTIgcmVzdWx0cyAKcmVzX2Rkc19rbwoKIyBzdW1tYXJ5IHJlc3VsdHMgb2YgcmVzX2Rkc19rbyAod2l0aCByZS1sZXZlbCBvZiB0cmVhdG1lbnQgPSAwIGZvciBrbm9ja291dF9saW5lKQpzdW1tYXJ5KHJlc19kZHNfa28sIDAuMDUpCgpgYGAKCiMjIyMgS09fMCB2cy4gS09fMjAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc19rbywgInRyZWF0bWVudCIsICIyMCIsICIwIikKYGBgCgojIyMjIEtPXzAgdnMuIEtPXzQwCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQpjb2VmX3Nocmlua2FnZV9jb250cmFzdChkZHNfa28sICJ0cmVhdG1lbnQiLCAiNDAiLCAiMCIpCmBgYAoKIyMjIyBLT18wIHZzLiBLT182MApgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KY29lZl9zaHJpbmthZ2VfY29udHJhc3QoZGRzX2tvLCAidHJlYXRtZW50IiwgIjYwIiwgIjAiKQpgYGAKCiMjIyMgS09fMCB2cy4gS09fODAKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CmNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KGRkc19rbywgInRyZWF0bWVudCIsICI4MCIsICIwIikKYGBgCgojIENTViBOYW1pbmcgS2V5CioqMi81LzI1KioKClRoZSBmb2xsb3dpbmcgYXJlIGRlZmluZWQgdGVybXMgYW5kIGFjcm9ueW1zIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGlzdGluZ3Vpc2ggdGhlIENTViBmaWxlcyBwcm9kdWNlZCB2aWEgdGhlIGFzaHJfc2hyaW5rYWdlX2NvbnRyYXN0KCkgYW5kIGNvZWZfc2hyaW5rYWdlX2NvbnRyYXN0KCkgZnVuY3Rpb25zLiAKCiAgLSBBbGwgZmlsZSB0aXRsZXMgd2lsbCBjb25zaXN0ZW50bHkgaW5jbHVkZSAnZGVzZXEyXzFfMl92c18zLmNzdic6CiAgICAtICdkZXNlcTInIGlzIGluY2x1ZGVkIGFzIHRoYXQgaXMgdGhlIHBhY2thZ2Ugd2UgYXJlIHVzaW5nIHRvIGNvbmR1Y3QgdGhlc2UgYW5hbHlzZXMgYW5kIHRvIHByb2R1Y2UgdGhlIG9iamVjdCB1c2VkIGluIHRoZSBhbmFseXNlcy4KICAgIC0gJ3ZzJyBzaW1wbHkgbWVhbnMgdmVyc3VzIGFzIHRoaXMgZGVub3RlcyB0aGF0IHBhcnRpY3VsYXIgY29tcGFyaXNvbiB1c2VkIHRvIHByb2R1Y2UgdGhlIGRhdGEgaW4gc2FpZCBmaWxlLiAKICAgIC0gJy5jc3YnIHdpbGwgYmUgY29uc2lzdGVudCBhcyB3ZSBhcmUgd3JpdGluZyB0byBhIGNzdiBmaWxlOyBjYW4gYmUgY2hhbmdlZCB3aXRoaW4gdGhlIGZ1bmN0aW9uIHRvIHdoYXRldmVyIGZpbGUgZm9ybWF0IHByZWZlcnJlZC4KICAgIC0gRnVuY3Rpb25zIHV0aWxpemVzIHNuYWtlX2Nhc2UgZm9ybWF0IGJ5IHBsYWNpbmcgYW4gdW5kZXJzY29yZSBiZXR3ZWVuIGFsbCB0ZXJtcyBpbiB0aGUgdGl0bGUuIEl0IHdpbGwgb25seSBiZSBhIG1peCBvZiBzbmFrZV9jYXNlCiAgICBhbmQgb3RoZXIgZm9ybWF0dGluZyBpZiB5b3UgZG8gbm90IHVzZSBzbmFrZV9jYXNlIHRvIG5hbWUgcm93IHZhbHVlcyBpbiB5b3VyIGNvbERhdGEgZGF0YSBmcmFtZS4gCiAgICAtIEVhY2ggbnVtYmVyIHJlcHJlc2VudHMgYSBwYXJ0IG9mIHlvdXIgY29udHJhc3QgcGFyYW1ldGVycyB1c2VkIGluIGVpdGhlciBmdW5jdGlvbiAoaS5lIHRoZSBudW1iZXJzIHdpbGwgbm90IGFjdHVhbGx5IGJlIGluIHlvdXIKICAgIHNhdmVkIGZpbGUgbmFtZSk6CiAgICAgIC0gMSA9IHRoZSBuYW1lIG9mIHlvdXIgdXNlZCAnZGRzJyBvYmplY3QgCiAgICAgIC0gMiA9IHRoZSBmaXJzdCBmYWN0b3IgbGV2ZWwgdXNlZCBpbiB5b3VyIGNvbnRyYXN0IAogICAgICAtIDMgPSB0aGUgc2Vjb25kIGZhY3RvciBsZXZlbCB1c2VkIGluIHlvdXIgY29udHJhc3QgCiAgCktleSBUaXRsZSBUZXJtczogCgogIC0gZGVzZXEyID0gcGFja2FnZSB1c2VkIHRvIHJ1biBhbmFseXNpcyAKICAKICAtICdkZHMnIE9iamVjdCBOYW1lczogCiAgICAtIGRkc19jb250cm9scyA9IHRoZSAnZGRzJyBvYmplY3QgdXNlZCBmb3IgYW5hbHlzaXMgb25seSBjb25zaXN0ZWQgb2YgdGhlIGNvbnRyb2wgZGF0YSBmb3IgYm90aCB3aWxkX3R5cGUgYW5kIGtub2Nrb3V0X2xpbmUgc2FtcGxlcwogICAgLSBkZHNfa28gPSB0aGUgJ2Rkcycgb2JqZWN0IHVzZWQgZm9yIGFuYWx5c2lzIG9ubHkgY29uc2lzdGVkIG9mIHRoZSBrbm9ja291dF9saW5lIGRhdGEKICAgIC0gZGRzX3d0ID0gdGhlICdkZHMnIG9iamVjdCB1c2VkIGZvciBhbmFseXNpcyBvbmx5IGNvbnNpc3RlZCBvZiB0aGUgd2lsZF90eXBlIGRhdGEKICAgIC0gZGRzX3RyZWF0bWVudCA9IHRoZSAnZGRzJyBvYmplY3QgdXNlZCBmb3IgYW5hbHlzaXMgY29uc2lzdGVkIG9mIGFsbCBzYW1wbGVzIChpbmNsdWRpbmcgY29udHJvbHMpCiAgCiAgLSBDb250cmFzdCBQYXJhbWV0ZXJzOiAKICAgIC0ga25vY2tvdXRfbGluZSA9IGdlbm90eXBlIGZhY3RvciBsZXZlbCBmb3Iga25vY2tvdXQtbGluZSBzYW1wbGVzIAogICAgLSB3aWxkX3R5cGUgPSBnZW5vdHlwZSBmYWN0b3IgbGV2ZWwgZm9yIHdpbGQtdHlwZSBzYW1wbGVzIAogICAgLSAwID0gdHJlYXRtZW50IGZhY3RvciBsZXZlbCBmb3IgY29udHJvbCBzYW1wbGVzIChubyB0cmVhdG1lbnQpCiAgICAtIDIwID0gdHJlYXRtZW50IGZhY3RvciBsZXZlbCBmb3Igc2FtcGxlcyB0cmVhdGVkIHdpdGggMjAgdU0gCiAgICAtIDQwID0gdHJlYXRtZW50IGZhY3RvciBsZXZlbCBmb3Igc2FtcGxlcyB0cmVhdGVkIHdpdGggNDAgdU0gCiAgICAtIDYwID0gdHJlYXRtZW50IGZhY3RvciBsZXZlbCBmb3Igc2FtcGxlcyB0cmVhdGVkIHdpdGggNjAgdU0gCiAgICAtIDgwID0gdHJlYXRtZW50IGZhY3RvciBsZXZlbCBmb3Igc2FtcGxlcyB0cmVhdGVkIHdpdGggODAgdU0gCiAgICAKRm9yIGV4YW1wbGUsIG9uZSBvZiB0aGUgY3N2IGZpbGVzIHByb2R1Y2VkIGZyb20gdGhlIGFib3ZlIGFuYWx5c2VzIGhhZCB0aGUgdGl0bGUgb2Y6IGRlc2VxMl9kZHNfa29fMjBfdnNfMC5jc3YKCiAgLSBUaGUgJ2Rkcycgb2JqZWN0IHVzZWQgY29uc2lzdGVkIG9mIG9ubHkgdGhlIGtub2Nrb3V0X2xpbmUgc2FtcGxlcyBhbmQgaXMgdGh1cyBuYW1lZCAnZGRzX2tvJyAoa28gPSBrbm9ja291dCkuCiAgLSBUaGlzIGZpbGUgY29udGFpbnMgdGhlIGNvbnRyYXN0IHJlc3VsdHMgb2YgdGhlIGtub2Nrb3V0X2xpbmUgc2FtcGxlcyB0cmVhdGVkIHdpdGggMjAgdU0gYWdhaW5zdCB0aGUgY29udHJvbCBzYW1wbGUgd2l0aCAwIHVNLiAKICAKVGhlcmVmb3JlLCB0aGlzIENTViBmaWxlIGNvbnRhaW5zIHRoZSByZXN1bHRpbmcgY29udHJhc3QgYmV0d2VlbiB0aGUga25vY2tvdXRfbGluZSBzYW1wbGUgdHJlYXRlZCB3aXRoIDIwIHVNIGFuZCB0aGUga25vY2tvdXRfbGluZSBjb250cm9sIHNhbXBsZSB3aXRoIDAgdU0uIAoKIyA4KSBHZW5lIE9udG9sb2d5IEVucmljaG1lbnQgQW5hbHlzaXMKKioyLzE4LzI1KioKCkJlIHN1cmUgdG8gaW5zdGFsbCBhbmQgcnVuIHRoZSBmb2xsb3dpbmcgbGlicmFyaWVzIGZvciB0aGUgYW5hbHlzaXM6CmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQojIGJlIHN1cmUgdG8gcnVuIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikgYW5kIHVzZSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgicGFja2FnZSIpIG9yZGVyIHRvIGluc3RhbGwgYW5kIHJ1biB0aGUgZm9sbG93aW5nIGxpYnJhcmllcwpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShBbm5vdGF0aW9uRGJpKSAjIHdhcm5pbmc6IHdpbGwgbWFzayBzZWxlY3QoKSBmcm9tIGRweWxyIApsaWJyYXJ5KG9yZy5BdC50YWlyLmRiKSAjIGRhdGFiYXNlIHVzZWQgZm9yIGFyYWJpZG9wc2lzCmBgYAoKIyMgUmVhZCBpbiBJbmRpdmlkdWFsIFRyZWF0bWVudCBDb21wYXJpc29uIENTViBGaWxlcyAKSW4gb3JkZXIgdG8gY29uZHVjdCBHTyBlbnJpY2htZW50IGFuYWx5c2lzLCB3ZSB3aWxsIG5lZWQgdG8gcmVhZCBpbiBvdXIgcHJvZHVjZWQgQ1NWIGZpbGVzIChmcm9tIHNlY3Rpb24gIzcpIGFzIGRhdGEgZnJhbWVzLiBUaGUgZm9sbG93aW5nIGNodW5rIG9mIGNvZGUgd2lsbCBsb29wIHRocm91Z2ggb3VyIGRhdGEgZm9sZGVyIHRoYXQgY29udGFpbnMgdGhlIENTViBmaWxlcyBhbmQgc2F2ZSB0aGVtIGFzIGluZGl2aWR1YWwgZGF0YSBmcmFtZXMgaW4gb3VyIGdsb2JhbCBlbnZpcm9ubWVudCBmb3IgZnVydGhlciB1c2UuIAoKVGhlIGNvZGUgd2lsbCBrZWVwIHRoZSBzYW1lIG5hbWUgZm9yIHRoZSBmaWxlcyBidXQgY2hhbmdlIHRoZSBiZWdpbm5pbmcgcG9ydGlvbiBvZiAiZGVzZXEyXyIgdG8gImRmXyIgYW5kIHdpbGwgb25seSByZWFkIGluIHRob3NlIHJvd3Mgd2l0aCBhIHBhZGogPCAwLjA1LiBCZSBzdXJlIHRvIGFsc28gcmVwbGFjZSB0aGUgZmlsZSBwYXRoIHdoZW4gY3JlYXRpbmcgdGhlIGRhdGFfZm9sZGVyIHZhcmlhYmxlIHRvIHdoZXJldmVyIHlvdXIgQ1NWIGZpbGVzIHdlcmUgc2F2ZWQgKHdpbGwgYmUgdGhlIHNhbWUgcGF0aCBhcyB1c2VkIGluIHRoZSBjb2VmIGFuZCBhc2hyIGZ1bmN0aW9ucyBpZiB0aG9zZSB3ZXJlIHV0aWxpemVkKS4gCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQojIGRlZmluZSB0aGUgZGF0YSBmb2xkZXIgcGF0aApkYXRhX2ZvbGRlciA8LSAiLi4vZGF0YS9hcmFiaWRvcHNpc19kZXNlcTJfcmVzdWx0cy8iICAjIGNoYW5nZSB0byB5b3VyIGFjdHVhbCBkYXRhIGZvbGRlciBwYXRoIAoKIyBsaXN0IGFsbCBDU1YgZmlsZXMgaW4gdGhlIGZvbGRlcgpjc3ZfZmlsZXMgPC0gbGlzdC5maWxlcyhkYXRhX2ZvbGRlciwgcGF0dGVybiA9ICJcXC5jc3YkIiwgZnVsbC5uYW1lcyA9IFRSVUUpCgojIGF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpCgojIGxvb3AgdGhyb3VnaCBlYWNoIGZpbGUgYW5kIGNyZWF0ZSBhIGZpbHRlcmVkIGRhdGEgZnJhbWUgKHBhZGogPCAwLjA1KQpmb3IgKGZpbGUgaW4gY3N2X2ZpbGVzKSB7CiAgIyBleHRyYWN0IGZpbGUgbmFtZSB3aXRob3V0IGV4dGVuc2lvbgogIGRmX25hbWUgPC0gdG9vbHM6OmZpbGVfcGF0aF9zYW5zX2V4dChiYXNlbmFtZShmaWxlKSkKICAKICAjIHJlcGxhY2UgImRlc2VxMl8iIHdpdGggImRmXyIgaW4gdGhlIHZhcmlhYmxlIG5hbWUgKHNob3VsZCBhbHdheXMgd29yayBpZiB1c2luZyB0aGUgYXNociBhbmQgY29lZiBmdW5jdGlvbnMpCiAgZGZfbmFtZSA8LSBzdWIoIl5kZXNlcTJfIiwgImRmXyIsIGRmX25hbWUpCiAgCiAgIyByZWFkIHRoZSBDU1YgaW50byBhIGRhdGEgZnJhbWUKICBkZiA8LSByZWFkLmNzdihmaWxlLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgCiAgIyBmaWx0ZXIgcm93cyB3aGVyZSBjb2x1bW4gcGFkaiA8IDAuMDUgYW5kIHJlbW92ZSByb3dzIHdoZXJlIHBhZGogPSBOQSAKICBkZiA8LSBkZlshaXMubmEoZGYkcGFkaikgJiBkZiRwYWRqIDwgMC4wNSwgXQogIAogICMgc2F2ZSB0aGUgZGF0YSBmcmFtZXMgdG8gdGhlIGdsb2JhbCBlbnZpcm9ubWVudAogIGFzc2lnbihkZl9uYW1lLCBkZiwgZW52aXIgPSAuR2xvYmFsRW52KQp9CmBgYAoKIyMgR09fcmVzdWx0cygpIEZ1bmN0aW9uIApGdW5jdGlvbiBtYWRlIHRvIGFsbG93IGZvciBtb2R1bGFyIEdlbmUgT250b2xvZ3kgRW5yaWNobWVudCBhbmFseXNpcyByZXN1bHQgb3V0cHV0LiBUaGUgZnVuY3Rpb24ncyBwYXJhbWV0ZXJzIGFyZSBhcyBmb2xsb3dzOgoKICAtIGRhdGFmcmFtZSA9IGRhdGFmcmFtZSBvZiBEZXNlcTIgcmVzdWx0cyBvZiBhIHNwZWNpZmljIGNvbXBhcmlzb24KICAtIHVwX29yX2Rvd24gPSBpbnB1dCAidXAiIHRvIHN1YnNldCB1cC1yZWd1bGF0ZWQgZ2VuZXMgKCtsb2cyRm9sZENoYW5nZSkgb3IgImRvd24iIHRvIHN1YnNldCBkb3duLXJlZ3VsYXRlZCBnZW5lcwogICgtbG9nMkZvbGRDaGFuZ2UpCiAgLSBkYXRhYmFzZSA9IGNob3NlbiBkYXRhYmFzZSB1c2VkIGZvciB0aGUgZ2VuZSBhbm5vdGF0aW9uIChwdWxsZWQgZnJvbSBCaW9jTWFuYWdlcjsgZGVmYXVsdCBpcyBvcmcuQXQudGFpci5kYiB3aGljaCBhCiAgZGF0YWJhc2UgZm9yIGFyYWJpZG9wc2lzKQogIC0ga2V5ID0ga2V5VHlwZSB1c2VkIGZvciB0aGUgY29ycmVjdCBvcmdhbmlzbSBhbmQgZGF0YWJhc2UgKGRlZmF1bHQgaXMgIlRBSVIiIHdoaWNoIGlzIGZvciBhcmFiaWRvcHNpcykKICAtIG9udF9jYXRlZ29yeSA9IHRoZSBtYWpvciBjYXRlZ29yeSB1c2VkIHRvIGRpY3RhdGUgdGhlIGdlbmUgb250b2xvZ3kgYW5hbHlzaXMKICAgIC0gQlAgPSBCaW9sb2dpY2FsIFByb2Nlc3NlcyAob3VyIGRlZmF1bHQgZm9yIHRoaXMgZnVuY3Rpb24pCiAgICAtIE1GID0gTW9sZWN1bGFyIEZ1bmN0aW9uCiAgICAtIENDID0gQ2VsbHVsYXIgQ29tcG9uZW50IApgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KR09fcmVzdWx0cyA8LSBmdW5jdGlvbihkYXRhZnJhbWUsIHVwX29yX2Rvd24sIGRhdGFiYXNlID0gIm9yZy5BdC50YWlyLmRiIiwga2V5ID0gIlRBSVIiLCBvbnRfY2F0ZWdvcnkgPSAiQlAiKSB7CiAgIyByZW1vdmUgYW55IHN1ZmZpeCAobGlrZSAiLjEiKSBmcm9tIHRoZSBHZW5lSURzICh3b24ndCB3b3JrIG90aGVyd2lzZSkKICBkYXRhZnJhbWUkR2VuZUlEIDwtIHN1YigiXFwuLioiLCAiIiwgZGF0YWZyYW1lJEdlbmVJRCkKICAKICAjIGZpbHRlciB1cC1yZWd1bGF0aW9uIG9yIGRvd24tcmVndWxhdGlvbiBiYXNlZCBvbiB1cF9vcl9kb3duCiAgaWYgKHVwX29yX2Rvd24gPT0gInVwIikgewogICAgZ2VuZXNfdG9fdGVzdCA8LSBkYXRhZnJhbWUkR2VuZUlEW2RhdGFmcmFtZSRsb2cyRm9sZENoYW5nZSA+IDBdCiAgfSBlbHNlIGlmICh1cF9vcl9kb3duID09ICJkb3duIikgewogICAgZ2VuZXNfdG9fdGVzdCA8LSBkYXRhZnJhbWUkR2VuZUlEW2RhdGFmcmFtZSRsb2cyRm9sZENoYW5nZSA8IDBdCiAgfSBlbHNlIHsKICAgIHN0b3AoIkludmFsaWQgdmFsdWUgZm9yICd1cF9vcl9kb3duJy4gVXNlICd1cCcgb3IgJ2Rvd24nLiIpCiAgfQogIAogICMgcGVyZm9ybSBHTyBlbnJpY2htZW50IGFuYWx5c2lzCiAgZ29fcmVzdWx0cyA8LSBlbnJpY2hHTyhnZW5lID0gZ2VuZXNfdG9fdGVzdCwgT3JnRGIgPSBkYXRhYmFzZSwga2V5VHlwZSA9IGtleSwgb250ID0gb250X2NhdGVnb3J5KQogIAogIHJldHVybihnb19yZXN1bHRzKQogIAp9CmBgYAoKIyMgc2F2ZV9HT19yZXN1bHRzKCkgRnVuY3Rpb24gClRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byBjb252ZXJ0IHRoZSBHTyBhbmFseXNpcyByZXN1bHRzIGludG8gYSBtb3JlIHJlYWRlci1mcmllbmRseSBkYXRhIGZyYW1lLCB3cml0ZSB0aGUgY29udGVudHMgdG8gYSBDU1YgZmlsZSwgYW5kIHRoZW4gc29ydCB0aGVtIGludG8gcmVzcGVjdGl2ZSBmb2xkZXJzIGZvciBlaXRoZXIgInVwIiBvciAiZG93biIgcmVndWxhdGlvbi4gCgogIC0gZ29fcmVzdWx0ID0gY2hvc2VuIEdPIHJlc3VsdHMgb2JqZWN0IAogIC0gZGlyZWN0aW9uID0gZGlyZWN0aW9uIG9mIGdlbmUgcmVndWxhdGlvbiAoInVwIiBvciAiZG93biIpIHVzZWQgdG8gZGV0ZXJtaW5lIHRoZSBmb2xkZXIgc29ydGluZyBsb2NhdGlvbiAKICAtIGZpbGVfcGF0aCA9IHBhdGggdG8gcGFyZW50IGRpcmVjdG9yeSBvZiB3aGVyZSB5b3VyICJ1cCIgb3IgImRvd24iIGZvbGRlcnMgYXJlIHByb2R1Y2VkIChkZWZhdWx0IGlzIG15IHBhcmVudCBkaXJlY3RvcnkpCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQpzYXZlX0dPX3Jlc3VsdHMgPC0gZnVuY3Rpb24oZ29fcmVzdWx0LCBkaXJlY3Rpb24sIGZpbGVfcGF0aCA9ICIuLi9kYXRhL2FyYWJpZG9wc2lzX0dPX3Jlc3VsdHMvIikgewogICMgY29udmVydCB0aGUgR08gcmVzdWx0cyBvYmplY3QgdG8gYSBkYXRhIGZyYW1lCiAgZ29fcmVzdWx0X2RmIDwtIGFzLmRhdGEuZnJhbWUoZ29fcmVzdWx0KQogIAogICMgZ2V0IHRoZSBnb19yZXN1bHQgdmFyaWFibGUgbmFtZSBhcyBhIHN0cmluZwogIG9iamVjdF9uYW1lIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShnb19yZXN1bHQpKQogIAogICMgZGVmaW5lIHRoZSBkaXJlY3RvcnkgYmFzZWQgdGhlIGdlbmUgcmVndWxhdGlvbiBkaXJlY3Rpb24gKCJ1cCIgb3IgImRvd24iKQogIG91dHB1dF9mb2xkZXIgPC0gcGFzdGUwKGZpbGVfcGF0aCwgZGlyZWN0aW9uLCAiX3Jlc3VsdHMvIikKICAKICAjIGNyZWF0ZSB0aGUgZm9sZGVyIGlmIGl0IGRvZXNuJ3QgZXhpc3QKICBkaXIuY3JlYXRlKG91dHB1dF9mb2xkZXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQogIAogICMgZGVmaW5lIHRoZSBvdXRwdXQgZmlsZSBuYW1lIHVzaW5nIHRoZSBnb19yZXN1bHQgdmFyaWFibGUgbmFtZSAoc28gbmFtZSBpdCBzb21ldGhpbmcgdXNlZnVsKQogIG91dHB1dF9maWxlIDwtIHBhc3RlMChvdXRwdXRfZm9sZGVyLCBvYmplY3RfbmFtZSwgIi5jc3YiKQogIAogICMgd3JpdGUgdGhlIGRhdGEgZnJhbWUgdG8gYSBDU1YgZmlsZQogIHdyaXRlLmNzdihnb19yZXN1bHRfZGYsIGZpbGUgPSBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpIAp9CmBgYAoKIyMgQW5ub3RhdGluZyBHZW5lcyAKVGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgdXRpbGl6ZSBjbHVzdGVyUHJvZmlsZXIsIEFubm90YXRpb25EYmksIHRoZSBHT19yZXN1bHRzKCksIGFuZCBzYXZlX0dPX3Jlc3VsdHMoKSBmdW5jdGlvbnMgdG8gYW5ub3RhdGUgdGhlIGdlbmVzIGZvdW5kIGluIGVhY2ggb2YgdGhlIGluZGl2aWR1YWwgQ1NWIGZpbGVzIHdlIGhhdmUganVzdCByZWFkIGludG8gZGF0YSBmcmFtZXMuIFdlIHdpbGwgc2VsZWN0IGZvciBib3RoIHVwLXJlZ3VsYXRlZCAoK2xvZzJGb2xkQ2hhbmdlKSBhbmQgZG93bi1yZWd1bGF0ZWQgKC1sb2cyRm9sZENoYW5nZSkgZm9yIGVhY2ggQ1NWIGZpbGUuIFdlIHdpbGwgYWxzbyBiZSBjb25kdWN0aW5nIHRoaXMgb24gZWFjaCBvZiB0aGUgdGhyZWUgZ2VuZSBvbnRvbG9neSBjYXRlZ29yaWVzIChiaW9sb2dpY2FsIHByb2Nlc3NlcywgbW9sZWN1bGFyIGZ1bmN0aW9ucywgY2VsbHVsYXIgY29tcG9uZW50cykuCgogIC0gV2Ugd2lsbCBoYXZlIGRvdWJsZSB0aGUgYW1vdW50IG9mIENTViBmaWxlcyBzcGxpdCBpbnRvIHR3byBkaWZmZXJlbnQgZm9sZGVycy4KCktPIFRyZWF0bWVudCB2cy4gV1QgVHJlYXRtZW50OgpgYGB7cn0KIyBXVF8yMCB2cy4gS09fMjAgCnVwX0JQX2tvXzIwX3ZzX3d0XzIwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb18yMF92c193dF8yMCwgInVwIikgIyBhY3R1YWwgR08gcmVzdWx0cyAodXAtcmVndWxhdGVkKS9kZWZhdWx0IG9udF9jYXRlZ29yeSA9ICJCUCIKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX2tvXzIwX3ZzX3d0XzIwX0dPLCAidXAiKSAjIEdPIHJlc3VsdHMgY29udmVydGVkIHRvIGEgZGF0YWZyYW1lIGFuZCBzYXZlZCB0byBhIENTViBmaWxlIApkb3duX0JQX2tvXzIwX3ZzX3d0XzIwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb18yMF92c193dF8yMCwgImRvd24iKSAjIChkb3duLXJlZ3VsYXRlZCkKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfa29fMjBfdnNfd3RfMjBfR08sICJkb3duIikKCnVwX01GX2tvXzIwX3ZzX3d0XzIwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb18yMF92c193dF8yMCwgInVwIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKHVwX01GX2tvXzIwX3ZzX3d0XzIwX0dPLCAidXAiKQpkb3duX01GX2tvXzIwX3ZzX3d0XzIwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb18yMF92c193dF8yMCwgImRvd24iLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9NRl9rb18yMF92c193dF8yMF9HTywgImRvd24iKQoKdXBfQ0Nfa29fMjBfdnNfd3RfMjBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzIwX3ZzX3d0XzIwLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0Nfa29fMjBfdnNfd3RfMjBfR08sICJ1cCIpCmRvd25fQ0Nfa29fMjBfdnNfd3RfMjBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzIwX3ZzX3d0XzIwLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyhkb3duX0NDX2tvXzIwX3ZzX3d0XzIwX0dPLCAiZG93biIpCgoKCiMgV1RfNDAgdnMuIEtPXzQwCnVwX0JQX2tvXzQwX3ZzX3d0XzQwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb180MF92c193dF80MCwgInVwIikKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX2tvXzQwX3ZzX3d0XzQwX0dPLCAidXAiKQpkb3duX0JQX2tvXzQwX3ZzX3d0XzQwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb180MF92c193dF80MCwgImRvd24iKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9CUF9rb180MF92c193dF80MF9HTywgImRvd24iKQoKdXBfTUZfa29fNDBfdnNfd3RfNDBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzQwX3ZzX3d0XzQwLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfa29fNDBfdnNfd3RfNDBfR08sICJ1cCIpCmRvd25fTUZfa29fNDBfdnNfd3RfNDBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzQwX3ZzX3d0XzQwLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyhkb3duX01GX2tvXzQwX3ZzX3d0XzQwX0dPLCAiZG93biIpCgp1cF9DQ19rb180MF92c193dF80MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fNDBfdnNfd3RfNDAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyh1cF9DQ19rb180MF92c193dF80MF9HTywgInVwIikKZG93bl9DQ19rb180MF92c193dF80MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fNDBfdnNfd3RfNDAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfa29fNDBfdnNfd3RfNDBfR08sICJkb3duIikKCgoKIyBXVF82MCB2cy4gS09fNjAgCnVwX0JQX2tvXzYwX3ZzX3d0XzYwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb182MF92c193dF82MCwgInVwIikKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX2tvXzYwX3ZzX3d0XzYwX0dPLCAidXAiKQpkb3duX0JQX2tvXzYwX3ZzX3d0XzYwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb182MF92c193dF82MCwgImRvd24iKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9CUF9rb182MF92c193dF82MF9HTywgImRvd24iKQoKdXBfTUZfa29fNjBfdnNfd3RfNjBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzYwX3ZzX3d0XzYwLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfa29fNjBfdnNfd3RfNjBfR08sICJ1cCIpCmRvd25fTUZfa29fNjBfdnNfd3RfNjBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzYwX3ZzX3d0XzYwLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyhkb3duX01GX2tvXzYwX3ZzX3d0XzYwX0dPLCAiZG93biIpCgp1cF9DQ19rb182MF92c193dF82MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fNjBfdnNfd3RfNjAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyh1cF9DQ19rb182MF92c193dF82MF9HTywgInVwIikKZG93bl9DQ19rb182MF92c193dF82MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fNjBfdnNfd3RfNjAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfa29fNjBfdnNfd3RfNjBfR08sICJkb3duIikKCgoKIyBXVF84MCB2cy4gS09fODAgCnVwX0JQX2tvXzgwX3ZzX3d0XzgwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb184MF92c193dF84MCwgInVwIikKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX2tvXzgwX3ZzX3d0XzgwX0dPLCAidXAiKQpkb3duX0JQX2tvXzgwX3ZzX3d0XzgwX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3RyZWF0bWVudF9rb184MF92c193dF84MCwgImRvd24iKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9CUF9rb184MF92c193dF84MF9HTywgImRvd24iKQoKdXBfTUZfa29fODBfdnNfd3RfODBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzgwX3ZzX3d0XzgwLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfa29fODBfdnNfd3RfODBfR08sICJ1cCIpCmRvd25fTUZfa29fODBfdnNfd3RfODBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfdHJlYXRtZW50X2tvXzgwX3ZzX3d0XzgwLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyhkb3duX01GX2tvXzgwX3ZzX3d0XzgwX0dPLCAiZG93biIpCgp1cF9DQ19rb184MF92c193dF84MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fODBfdnNfd3RfODAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyh1cF9DQ19rb184MF92c193dF84MF9HTywgInVwIikKZG93bl9DQ19rb184MF92c193dF84MF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc190cmVhdG1lbnRfa29fODBfdnNfd3RfODAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfa29fODBfdnNfd3RfODBfR08sICJkb3duIikKYGBgCgpPbmx5IENvbnRyb2xzOgpgYGB7cn0KIyBXVF8wIHZzLiBLT18wCnVwX0JQX2NvbnRyb2xfa29fMF92c193dF8wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2NvbnRyb2xzX2tub2Nrb3V0X2xpbmVfdnNfd2lsZF90eXBlLCAidXAiKQpzYXZlX0dPX3Jlc3VsdHModXBfQlBfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJ1cCIpCmRvd25fQlBfY29udHJvbF9rb18wX3ZzX3d0XzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfY29udHJvbHNfa25vY2tvdXRfbGluZV92c193aWxkX3R5cGUsICJkb3duIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJkb3duIikKCnVwX01GX2NvbnRyb2xfa29fMF92c193dF8wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2NvbnRyb2xzX2tub2Nrb3V0X2xpbmVfdnNfd2lsZF90eXBlLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJ1cCIpCmRvd25fTUZfY29udHJvbF9rb18wX3ZzX3d0XzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfY29udHJvbHNfa25vY2tvdXRfbGluZV92c193aWxkX3R5cGUsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fTUZfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJkb3duIikKCnVwX0NDX2NvbnRyb2xfa29fMF92c193dF8wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2NvbnRyb2xzX2tub2Nrb3V0X2xpbmVfdnNfd2lsZF90eXBlLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0NfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJ1cCIpCmRvd25fQ0NfY29udHJvbF9rb18wX3ZzX3d0XzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfY29udHJvbHNfa25vY2tvdXRfbGluZV92c193aWxkX3R5cGUsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0NfY29udHJvbF9rb18wX3ZzX3d0XzBfR08sICJkb3duIikKYGBgCgpPbmx5IFdpbGQtVHlwZSBTYW1wbGVzOgpgYGB7cn0KIyBXVF8wIHZzLiBXVF8yMCAKdXBfQlBfd3RfMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF8yMF92c18wLCAidXAiKQpzYXZlX0dPX3Jlc3VsdHModXBfQlBfd3RfMjBfdnNfMF9HTywgInVwIikKZG93bl9CUF93dF8yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzIwX3ZzXzAsICJkb3duIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfd3RfMjBfdnNfMF9HTywgImRvd24iKQoKdXBfTUZfd3RfMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF8yMF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfd3RfMjBfdnNfMF9HTywgInVwIikKZG93bl9NRl93dF8yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzIwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fTUZfd3RfMjBfdnNfMF9HTywgImRvd24iKQoKdXBfQ0Nfd3RfMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF8yMF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0Nfd3RfMjBfdnNfMF9HTywgInVwIikKZG93bl9DQ193dF8yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzIwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfd3RfMjBfdnNfMF9HTywgImRvd24iKQoKCgojIFdUXzAgdnMuIFdUXzQwIAp1cF9CUF93dF80MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzQwX3ZzXzAsICJ1cCIpCnNhdmVfR09fcmVzdWx0cyh1cF9CUF93dF80MF92c18wX0dPLCAidXAiKQpkb3duX0JQX3d0XzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNDBfdnNfMCwgImRvd24iKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9CUF93dF80MF92c18wX0dPLCAiZG93biIpCgp1cF9NRl93dF80MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzQwX3ZzXzAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyh1cF9NRl93dF80MF92c18wX0dPLCAidXAiKQpkb3duX01GX3d0XzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNDBfdnNfMCwgImRvd24iLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9NRl93dF80MF92c18wX0dPLCAiZG93biIpCgp1cF9DQ193dF80MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzQwX3ZzXzAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyh1cF9DQ193dF80MF92c18wX0dPLCAidXAiKQpkb3duX0NDX3d0XzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNDBfdnNfMCwgImRvd24iLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9DQ193dF80MF92c18wX0dPLCAiZG93biIpCgoKCiMgV1RfMCB2cy4gV1RfNjAgCnVwX0JQX3d0XzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNjBfdnNfMCwgInVwIikKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX3d0XzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fQlBfd3RfNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF82MF92c18wLCAiZG93biIpCnNhdmVfR09fcmVzdWx0cyhkb3duX0JQX3d0XzYwX3ZzXzBfR08sICJkb3duIikKCnVwX01GX3d0XzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNjBfdnNfMCwgInVwIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKHVwX01GX3d0XzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fTUZfd3RfNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF82MF92c18wLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyhkb3duX01GX3d0XzYwX3ZzXzBfR08sICJkb3duIikKCnVwX0NDX3d0XzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfd3RfNjBfdnNfMCwgInVwIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKHVwX0NDX3d0XzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fQ0Nfd3RfNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF82MF92c18wLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyhkb3duX0NDX3d0XzYwX3ZzXzBfR08sICJkb3duIikKCgoKIyBXVF8wIHZzLiBXVF84MCAKdXBfQlBfd3RfODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF84MF92c18wLCAidXAiKQpzYXZlX0dPX3Jlc3VsdHModXBfQlBfd3RfODBfdnNfMF9HTywgInVwIikKZG93bl9CUF93dF84MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzgwX3ZzXzAsICJkb3duIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfd3RfODBfdnNfMF9HTywgImRvd24iKQoKdXBfTUZfd3RfODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF84MF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfd3RfODBfdnNfMF9HTywgInVwIikKZG93bl9NRl93dF84MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzgwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fTUZfd3RfODBfdnNfMF9HTywgImRvd24iKQoKdXBfQ0Nfd3RfODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc193dF84MF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0Nfd3RfODBfdnNfMF9HTywgInVwIikKZG93bl9DQ193dF84MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX3d0XzgwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfd3RfODBfdnNfMF9HTywgImRvd24iKQpgYGAKCk9ubHkgS25vY2tvdXQtTGluZSBTYW1wbGVzOgpgYGB7cn0KIyBLT18wIHZzLiBLT18yMCAKdXBfQlBfa29fMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb18yMF92c18wLCAidXAiKQpzYXZlX0dPX3Jlc3VsdHModXBfQlBfa29fMjBfdnNfMF9HTywgInVwIikKZG93bl9CUF9rb18yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzIwX3ZzXzAsICJkb3duIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfa29fMjBfdnNfMF9HTywgImRvd24iKQoKdXBfTUZfa29fMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb18yMF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfa29fMjBfdnNfMF9HTywgInVwIikKZG93bl9NRl9rb18yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzIwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fTUZfa29fMjBfdnNfMF9HTywgImRvd24iKQoKdXBfQ0Nfa29fMjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb18yMF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0Nfa29fMjBfdnNfMF9HTywgInVwIikKZG93bl9DQ19rb18yMF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzIwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfa29fMjBfdnNfMF9HTywgImRvd24iKQoKCgojIEtPXzAgdnMuIEtPXzQwIAp1cF9CUF9rb180MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzQwX3ZzXzAsICJ1cCIpCnNhdmVfR09fcmVzdWx0cyh1cF9CUF9rb180MF92c18wX0dPLCAidXAiKQpkb3duX0JQX2tvXzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNDBfdnNfMCwgImRvd24iKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9CUF9rb180MF92c18wX0dPLCAiZG93biIpCgp1cF9NRl9rb180MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzQwX3ZzXzAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyh1cF9NRl9rb180MF92c18wX0dPLCAidXAiKQpkb3duX01GX2tvXzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNDBfdnNfMCwgImRvd24iLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9NRl9rb180MF92c18wX0dPLCAiZG93biIpCgp1cF9DQ19rb180MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzQwX3ZzXzAsICJ1cCIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyh1cF9DQ19rb180MF92c18wX0dPLCAidXAiKQpkb3duX0NDX2tvXzQwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNDBfdnNfMCwgImRvd24iLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHMoZG93bl9DQ19rb180MF92c18wX0dPLCAiZG93biIpCgoKCiMgS09fMCB2cy4gS09fNjAgCnVwX0JQX2tvXzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNjBfdnNfMCwgInVwIikKc2F2ZV9HT19yZXN1bHRzKHVwX0JQX2tvXzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fQlBfa29fNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb182MF92c18wLCAiZG93biIpCnNhdmVfR09fcmVzdWx0cyhkb3duX0JQX2tvXzYwX3ZzXzBfR08sICJkb3duIikKCnVwX01GX2tvXzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNjBfdnNfMCwgInVwIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKHVwX01GX2tvXzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fTUZfa29fNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb182MF92c18wLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJNRiIpCnNhdmVfR09fcmVzdWx0cyhkb3duX01GX2tvXzYwX3ZzXzBfR08sICJkb3duIikKCnVwX0NDX2tvXzYwX3ZzXzBfR08gPC0gR09fcmVzdWx0cyhkZl9kZHNfa29fNjBfdnNfMCwgInVwIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKHVwX0NDX2tvXzYwX3ZzXzBfR08sICJ1cCIpCmRvd25fQ0Nfa29fNjBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb182MF92c18wLCAiZG93biIsIG9udF9jYXRlZ29yeSA9ICJDQyIpCnNhdmVfR09fcmVzdWx0cyhkb3duX0NDX2tvXzYwX3ZzXzBfR08sICJkb3duIikKCgoKIyBLT18wIHZzLiBLT184MCAKdXBfQlBfa29fODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb184MF92c18wLCAidXAiKQpzYXZlX0dPX3Jlc3VsdHModXBfQlBfa29fODBfdnNfMF9HTywgInVwIikKZG93bl9CUF9rb184MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzgwX3ZzXzAsICJkb3duIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQlBfa29fODBfdnNfMF9HTywgImRvd24iKQoKdXBfTUZfa29fODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb184MF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiTUYiKQpzYXZlX0dPX3Jlc3VsdHModXBfTUZfa29fODBfdnNfMF9HTywgInVwIikKZG93bl9NRl9rb184MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzgwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIk1GIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fTUZfa29fODBfdnNfMF9HTywgImRvd24iKQoKdXBfQ0Nfa29fODBfdnNfMF9HTyA8LSBHT19yZXN1bHRzKGRmX2Rkc19rb184MF92c18wLCAidXAiLCBvbnRfY2F0ZWdvcnkgPSAiQ0MiKQpzYXZlX0dPX3Jlc3VsdHModXBfQ0Nfa29fODBfdnNfMF9HTywgInVwIikKZG93bl9DQ19rb184MF92c18wX0dPIDwtIEdPX3Jlc3VsdHMoZGZfZGRzX2tvXzgwX3ZzXzAsICJkb3duIiwgb250X2NhdGVnb3J5ID0gIkNDIikKc2F2ZV9HT19yZXN1bHRzKGRvd25fQ0Nfa29fODBfdnNfMF9HTywgImRvd24iKQpgYGAKCiMjIGNvdW50X0dPX3Rlcm1zKCkgRnVuY3Rpb24gCioqMi8yNS8yNSoqCgpUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHByb2R1Y2VzIGEgQ1NWIGZpbGUvdGFibGUgdGhhdCBjb250YWlucyB0aGUgY291bnQgb2YgZ2VuZSBvbnRvbG9neSBkZXNjcmlwdGlvbiB0ZXJtcyBmcm9tIHRoZSBCUCwgTUYsIGFuZCBDQyBhbm5vdGF0aW9ucy4gSXQgZG9lcyBzbyBieSBwYXJzaW5nIHRocm91Z2ggdGhlIGFzc29jaWF0ZWQgInVwIiBhbmQgImRvd24iIHJlZ3VsYXRlZCBnZW5lIGFubm90YXRpb24gZmlsZXMgYW5kIGluc2VydHMgdGhlIHZhbHVlcyBpbnRvIHRoZWlyIGFwcHJvcHJpYXRlIHJvd3MvY29sdW1ucy4gSXQgbG9va3MgZm9yIHBhdHRlcm5zIGRlbm90ZWQgYnkgdGhlIHRpdGxlIG9mIHRoZSBDU1YgZmlsZXMgc2F2ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAgb2YgYW5ub3RhdGluZyBnZW5lcy4gSXQgc2VhcmNoZXMgYSBzcGVjaWZpYyBkaXJlY3RvcnkgKG15IHBlcnNvbmFsIG9uZSBpcyB0aGUgZGVmYXVsdCkgYW5kIHNlYXJjaGVzIGZvciBtb3JlIHNwZWNpZmljIGRpcmVjdG9yaWVzIHRoYXQgc2VwYXJhdGVkIHRoZSAidXAiIGFuZCAiZG93biIgcmVzdWx0cyAodGhlIGRlZmF1bHQgYXJlIHNldCB0byBteSBuYW1pbmcgY29udmVudGlvbikuIFRoZSB0YWJsZXMgd2lsbCBiZSBzYXZlZCB0byB0aGVpciBvd24gbmV3IGZvbGRlciAobmFtZWQgR09fdGVybV9jb3VudHMpIHdpdGhpbiB5b3VyIGNob3NlbiBkYXRhX2ZpbGVfcGF0aCBkaXJlY3RvcnkuIAoKICAtIFJlZ2FyZGxlc3MgaWYgeW91IGhhdmUgdGhpcyBlbnRpcmUgcmVwb3NpdG9yeSwgYW5kIGRvbid0IG5lZWQgdG8gc3BlY2lmeSBhbnkgZGlmZmVyZW50IGZpbGUgcGF0aHMsIHlvdSBtdXN0IGluY2x1ZGUgdGhlCiAgQ1NWIHRpdGxlIHBhdHRlcm4gdW5pcXVlIHRvIGVhY2ggZmlsZSBwcm9kdWNlZCBkdXJpbmcgdGhlIGFubm90YXRpbmcgZ2VuZXMgc3RlcC4gVGhvc2UgbmFtZXMgYXJlIG5vdCBkeW5hbWljLCB0aGVyZWZvcmUsCiAgaXQgd2lsbCBkZXBlbmQgY29tcGxldGVseSBvbiBob3cgeW91IG5hbWVkIHRoZW0geW91cnNlbGYgKGFzc3VtaW5nIHlvdSBjaGFuZ2VkIHRoZW0gZnJvbSB3aGF0IEkgb3JpZ2luYWxseSBoYWQgdGhlbSBhcyk7CiAgQkUgQ0FSRUZVTCEgSSBzdWdnZXN0IHlvdSBqdXN0IHVzZSB0aGUgY29kZSBhcyBpcyBhcyB0aGluZ3MgYXJlIGdldHRpbmcgY29tcGxpY2F0ZWQgdG8gYSBwb2ludCBJJ20gc3RydWdnbGluZyB0byBtYWtlCiAgZXZlcnl0aGluZyBkeW5hbWljLiAKICAKVGhlIGZ1bmN0aW9uJ3MgYXJndW1lbnRzIGFyZToKCiAgLSBkYXRhX2ZpbGVfcGF0aCA9IHRoZSBwYXRoIHRvIHRoZSBwYXJlbnQgZGlyZWN0b3J5IHRoYXQgY29udGFpbnMgeW91ciB1cC1yZWd1bGF0ZWQgYW5kIGRvd24tcmVndWxhdGVkIGdlbmUgYW5ub3RhdGlvbgogIHJlc3VsdHMgKGZvciBCUCwgTUYsIGFuZCBDQykuCiAgLSBjb21wYXJpc29uX3BhdHRlcm4gPSB0aGUgcGFydGljdWxhciBwYXR0ZXJuIChpLmUuIHBhcnQgb2YgdGhlIENTViBmaWxlIG5hbWUpIG9mIHRoZSBnZW5lIGFubm90YXRpb24gcmVzdWx0cwogICAgLSBCZSBzdXJlIHRvIG5vdCBpbmNsdWRlIGEgYmVnaW5uaW5nIG9yIGVuZGluZyB1bmRlcnNjb3JlICh0aGUgZnVuY3Rpb24gYWNjb3VudHMgZm9yIHRoYXQgYWxyZWFkeSkuCiAgLSB1cF9yZWd1bGF0ZWRfZGF0YSA9IHRoZSBuYW1lIG9mIHRoZSBjaGlsZCBkaXJlY3RvcnkgdGhhdCBjb250YWlucyB5b3VyIHVwLXJlZ3VsYXRlZCBkYXRhIGZpbGVzIAogIC0gZG93bl9yZWd1bGF0ZWRfZGF0YSA9IHRoZSBuYW1lIG9mIHRoZSBjaGlsZCBkaXJlY3RvcnkgdGhhdCBjb250YWlucyB5b3VyIGRvd24tcmVndWxhdGVkIGRhdGEgZmlsZXMgCiAgCk5vdGU6IHRoZXJlIGlzIGEgbG90IG9mIGRlYnVnZ2luZyBjb2RlIGluIHRoaXMgZnVuY3Rpb24gdG8gc2VlIGhvdyBpdCB3b3JrcyBhcyBJIHN0cnVnZ2xlZCB3cml0aW5nIGl0IGZvciBxdWl0ZSBzb21lIHRpbWUuCmBgYHtyfQpjb3VudF9HT190ZXJtcyA8LSBmdW5jdGlvbihkYXRhX2ZpbGVfcGF0aCA9ICIuLi9kYXRhL2FyYWJpZG9wc2lzX0dPX3Jlc3VsdHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFyaXNvbl9wYXR0ZXJuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfcmVndWxhdGVkX2RhdGEgPSAidXBfcmVzdWx0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3JlZ3VsYXRlZF9kYXRhID0gImRvd25fcmVzdWx0cyIpIHsKICAjIGRlZmluZSB0aGUgR08gY2F0ZWdvcmllcwogIGdvX2NhdGVnb3JpZXMgPC0gYygiQlAiLCAiTUYiLCAiQ0MiKQogIAogICMgaW5pdGlhbGl6ZSB0aGUgcmVzdWx0cyBtYXRyaXggCiAgcmVzdWx0c19tYXRyaXggPC0gbWF0cml4KDAsIG5yb3cgPSBsZW5ndGgoZ29fY2F0ZWdvcmllcyksIG5jb2wgPSAyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGdvX2NhdGVnb3JpZXMsIGMoInVwIiwgImRvd24iKSkpCiAgCiAgIyBoZWxwZXIgZnVuY3Rpb24gdXNlZCB0byBwcm9jZXNzIGZpbGVzIGluIGEgZ2l2ZW4gZm9sZGVyIGFuZCBkaXJlY3Rpb24gKCJ1cCIgb3IgImRvd24iKQogIHByb2Nlc3NfZmlsZXMgPC0gZnVuY3Rpb24oZm9sZGVyLCBkaXJlY3Rpb24sIHJlc3VsdHNfbWF0cml4KSB7CiAgICBmb3IgKGNhdGVnb3J5IGluIGdvX2NhdGVnb3JpZXMpIHsKICAgICAgIyBidWlsZCB0aGUgZXhhY3QgZmlsZSBuYW1lIHBhdHRlcm4KICAgICAgZmlsZV9wYXR0ZXJuIDwtIHBhc3RlMChkaXJlY3Rpb24sICJfIiwgY2F0ZWdvcnksICJfIiwgY29tcGFyaXNvbl9wYXR0ZXJuLCAiX0dPLmNzdiIpICMgdmVyeSBleGFjdCB0byB0aGUgbmFtaW5nIGNvbnZlbnRpb24gdXNlZCBmb3IgdGhlIGFubm90YXRpbmcgZ2VuZXMgc3RlcCAKICAgICAgCiAgICAgICMgbGlzdCBvdXQgZmlsZXMgbWF0Y2hpbmcgdGhlIG5hbWUgcGF0dGVybiAoc2hvdWxkIGJlIG9ubHkgb25lIHBlciBjYXRlZ29yeSAoQlAsIE1GLCBDQykpCiAgICAgIGZpbGVzIDwtIGxpc3QuZmlsZXMoZm9sZGVyLCBwYXR0ZXJuID0gZmlsZV9wYXR0ZXJuLCBmdWxsLm5hbWVzID0gVFJVRSkKICAgICAgCiAgICAgIGlmIChsZW5ndGgoZmlsZXMpID4gMCkgewogICAgICAgIGZpbGUgPC0gZmlsZXNbMV0gICMgdGFrZSB0aGUgaW5pdGlhbCBtYXRjaCAoc2hvdWxkIG9ubHkgYmUgb25lIGFueXdheXMpCiAgICAgICAgCiAgICAgICAgIyByZWFkIHRoZSBtYXRjaGluZyBDU1YgZmlsZQogICAgICAgIGRhdGEgPC0gcmVhZC5jc3YoZmlsZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogICAgICAgIAogICAgICAgICMgY291bnQgcm93cyB3aGVyZSBwLmFkanVzdCA8IDAuMDUgKHdpbGwgbWF0Y2ggdGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBHTyBEZXNjcmlwdGlvbiB0ZXJtcyk7IGlnbm9yZXMgTkEgdmFsdWVzIAogICAgICAgIGNvdW50IDwtIHN1bShkYXRhJHAuYWRqdXN0IDwgMC4wNSwgbmEucm0gPSBUUlVFKQogICAgICAgIAogICAgICAgICMgREVCVUc6IGNoZWNrIHRoZSBjb3VudCBhbmQgd2hlcmUgaXQgaXMgYmVpbmcgYXNzaWduZWQKICAgICAgICBwcmludChwYXN0ZSgiVXBkYXRpbmcgbWF0cml4IGZvciBjYXRlZ29yeToiLCBjYXRlZ29yeSwgImFuZCBkaXJlY3Rpb246IiwgZGlyZWN0aW9uKSkKICAgICAgICBwcmludChwYXN0ZSgiQ291bnQgdG8gYXNzaWduOiIsIGNvdW50KSkKICAgICAgICAKICAgICAgICAjIHN0b3JlIHJlc3VsdCBpbiBtYXRyaXgKICAgICAgICBpZiAoZGlyZWN0aW9uID09ICJ1cCIpIHsKICAgICAgICAgIHJlc3VsdHNfbWF0cml4W2NhdGVnb3J5LCAidXAiXSA8LSBjb3VudAogICAgICAgIH0gZWxzZSBpZiAoZGlyZWN0aW9uID09ICJkb3duIikgewogICAgICAgICAgcmVzdWx0c19tYXRyaXhbY2F0ZWdvcnksICJkb3duIl0gPC0gY291bnQKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgIyBERUJVRzogZGlzcGxheSB0aGUgbWF0cml4IGFmdGVyIGVhY2ggY291bnQgYXNzaWdubWVudAogICAgICAgIHByaW50KHJlc3VsdHNfbWF0cml4KSAgCiAgICAgIH0gZWxzZSB7CiAgICAgICAgIyBpZiBubyBmaWxlcyBhcmUgZm91bmQgKGkuZS4gaW5jb3JyZWN0IHBhdHRlcm4gaW5wdXQpLCBwcmludCBhIHdhcm5pbmcgZXJyb3IgCiAgICAgICAgd2FybmluZyhwYXN0ZSgiTm8gbWF0Y2hpbmcgZmlsZSBmb3VuZCBmb3IiLCBkaXJlY3Rpb24sIGNhdGVnb3J5LCAiaW4iLCBmb2xkZXIpKQogICAgICB9CiAgICB9CiAgICByZXR1cm4ocmVzdWx0c19tYXRyaXgpICAjIHJldHVybiB1cGRhdGVkIG1hdHJpeAogIH0KICAKICAjIGRlZmluZSB0aGUgb3V0cHV0IHBhdGgKICBvdXRwdXRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2ZpbGVfcGF0aCwgIkdPX3Rlcm1fY291bnRzIikKICAKICAjIGNyZWF0ZSB0aGUgZGlyZWN0b3J5IChpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QpCiAgZGlyLmNyZWF0ZShvdXRwdXRfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICAKICAjIHByb2Nlc3MgYm90aCB0aGUgInVwIiBhbmQgImRvd24iIENTViBmaWxlIHJlc3VsdHMgCiAgcmVzdWx0c19tYXRyaXggPC0gcHJvY2Vzc19maWxlcyhmaWxlLnBhdGgoZGF0YV9maWxlX3BhdGgsIHVwX3JlZ3VsYXRlZF9kYXRhKSwgInVwIiwgcmVzdWx0c19tYXRyaXgpCiAgcmVzdWx0c19tYXRyaXggPC0gcHJvY2Vzc19maWxlcyhmaWxlLnBhdGgoZGF0YV9maWxlX3BhdGgsIGRvd25fcmVndWxhdGVkX2RhdGEpLCAiZG93biIsIHJlc3VsdHNfbWF0cml4KQogIAogICMgREVCVUc6IGRpc3BsYXkgdGhlIGZpbmFsIHJlc3VsdHMgbWF0cml4CiAgcHJpbnQoIkZpbmFsIHJlc3VsdHNfbWF0cml4OiIpCiAgcHJpbnQocmVzdWx0c19tYXRyaXgpCiAgCiAgIyBkZWZpbmUgdGhlIG91dHB1dCBwYXRoIGFuZCBzYXZlIHRoZSByZXN1bHRzIG9uY2UgdGhlIGVudGlyZSBtYXRyaXggaXMgY29tcGxldGUgCiAgb3V0cHV0X2ZpbGUgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsIHBhc3RlMChjb21wYXJpc29uX3BhdHRlcm4sICJfR09fY291bnRzLmNzdiIpKQogIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdHNfbWF0cml4KSwgb3V0cHV0X2ZpbGUsIHJvdy5uYW1lcyA9IFRSVUUpCn0KYGBgCgpUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmtzIGFyZSB0aGUgcnVubmluZyBvZiB0aGUgY291bnRfR09fdGVybXMoKSBmdW5jdGlvbiB0byBwcm9kdWNlIGEgdGFibGUgZm9yIGVhY2ggY29tcGFyaXNvbi4KCiAtIFRoZSBpbml0aWFsIGNvZGUgY2h1bmsgd2lsbCBkaXNwbGF5IHdoYXQgdGhlIGFzc2lnbm1lbnQgdG8gdGhlIG1hdHJpeCBsb29rcyBsaWtlIGluIHRoZSB0ZXJtaW5hbC4gVGhlIHJlc3Qgb2YgdGhlCiBpbnN0YW5jZXMgd2lsbCBoaWRlIHRoZSBvdXRwdXQuIAoKS08gVHJlYXRtZW50IHZzLiBXVCBUcmVhdG1lbnQ6CmBgYHtyfQojIFdUXzIwIHZzLiBLT18yMCAKY291bnRfR09fdGVybXMoY29tcGFyaXNvbl9wYXR0ZXJuID0gImtvXzIwX3ZzX3d0XzIwIikKYGBgCgpgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KIyBXVF80MCB2cy4gS09fNDAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJrb180MF92c193dF80MCIpCgojIFdUXzYwIHZzLiBLT182MApjb3VudF9HT190ZXJtcyhjb21wYXJpc29uX3BhdHRlcm4gPSAia29fNjBfdnNfd3RfNjAiKQoKIyBXVF84MCB2cy4gS09fODAKY291bnRfR09fdGVybXMoY29tcGFyaXNvbl9wYXR0ZXJuID0gImtvXzgwX3ZzX3d0XzgwIikKYGBgCgpPbmx5IENvbnRyb2xzOgpgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KIyBXVF8wIHZzLiBLT18wCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJjb250cm9sX2tvXzBfdnNfd3RfMCIpCmBgYAoKT25seSBXaWxkLVR5cGUgU2FtcGxlczoKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CiMgV1RfMCB2cy4gV1RfMjAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJ3dF8yMF92c18wIikKCiMgV1RfMCB2cy4gV1RfNDAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJ3dF80MF92c18wIikKCiMgV1RfMCB2cy4gV1RfNjAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJ3dF82MF92c18wIikKCiMgV1RfMCB2cy4gV1RfODAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJ3dF84MF92c18wIikKYGBgCgpPbmx5IEtub2Nrb3V0LUxpbmUgU2FtcGxlczoKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CiMgS09fMCB2cy4gS09fMjAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJrb18yMF92c18wIikKCiMgS09fMCB2cy4gS09fNDAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJrb180MF92c18wIikKCiMgS09fMCB2cy4gS09fNjAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJrb182MF92c18wIikKCiMgS09fMCB2cy4gS09fODAgCmNvdW50X0dPX3Rlcm1zKGNvbXBhcmlzb25fcGF0dGVybiA9ICJrb184MF92c18wIikKYGBgCgojIyBwbG90X3RvcF9HT190ZXJtcygpIEZ1bmN0aW9uIAoqKkRvdWJsZSBjaGVjayB0aGF0IHRoaXMgd29ya3MgY29ycmVjdGx5IGJ5IGxvb2tpbmcgYXQgdGhlIHZhbHVlcyBkaXJlY3RseTsgSSB0aGluayBpdCBzdHJ1Z2dsZXMgd2hlbiB0aGVyZSBhcmUgbWF0Y2hpbmcgdmFsdWVzKioKClRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gdXNlcyBhIHNpbWlsYXIgbG9naWMgYXMgdGhlIGNvdW50X0dPX3Rlcm1zKCkgZnVuY3Rpb24uIFRoZSBkaWZmZXJlbmNlIGlzIHRoYXQgaW5zdGVhZCBvZiBtYWtpbmcgYSB0YWJsZSB3aXRoIHNpZ25pZmljYW50IEdPIHRlcm0gY291bnRzLCBpdCBtYWtlcyBhIGhvcml6b250YWwgYmFyIHBsb3QgdGhhdCBkaXNwbGF5cyB0aGUgbW9zdCBzaWduaWZpY2FudCBHTyB0ZXJtcyBmb3IgZWFjaCBjYXRlZ29yeSAoQlAsIE1GLCBDQykgYWNyb3NzIGJvdGggdGhlIHVwIGFuZCBkb3duIHJlZ3VsYXRlZCBnZW5lIGFubm90YXRpb25zIGZvciBhIHBhcnRpY3VsYXIgY29tcGFyaXNvbi4gSW4gb3RoZXIgd29yZHMsIGl0IHBhcnNlcyB0aHJvdWdoIGFsbCBhc3NvY2lhdGVkIGRhdGEgc2V0cyBmb3IgdGhlIGNob3NlbiBjb21wYXJpc29uX3BhdHRlcm4gYW5kIGZpbmRzIHRoZSBzbWFsbGVzdCBwLmFkanVzdCB2YWx1ZXMgYWNyb3NzIHRoZSB1cCBhbmQgZG93biByZWd1bGF0ZWQgZ2VuZXMgY29tYmluZWQuIEl0IHRoZW4gc2VwYXJhdGVzIHRoZW0gYnkgY2F0ZWdvcnkuIAoKVGhlIGZ1bmN0aW9uJ3MgYXJndW1lbnRzIGFyZSB0aGUgc2FtZSBhcyB0aGUgY291bnRfR09fdGVybXMoKSBmdW5jdGlvbiwgaG93ZXZlciwgdGhlcmUgaXMgbm93IGFuIG9wdGlvbiB0byBzZWxlY3QgaG93IG1hbnkgc2lnbmlmaWNhbnQgdGVybXMgd2Ugd2FudCBpbiB0aGUgcGxvdC4gCgogIC0gdG9wX24gPSBob3cgbWFueSBzaWduaWZpY2FudCB0ZXJtcyB3ZSB3YW50IHRvIGluY2x1ZGUgaW4gdGhlIHBsb3QgKHRvcCAzLCB0b3AgNSwgdG9wIDcsIHRvcCAxMCwgZXRjLikKICAgIC0gRGVmYXVsdCBpcyA1IApgYGB7cn0KcGxvdF90b3BfR09fdGVybXMgPC0gZnVuY3Rpb24oZGF0YV9maWxlX3BhdGggPSAiLi4vZGF0YS9hcmFiaWRvcHNpc19HT19yZXN1bHRzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhcmlzb25fcGF0dGVybiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9uID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfcmVndWxhdGVkX2RhdGEgPSAidXBfcmVzdWx0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3JlZ3VsYXRlZF9kYXRhID0gImRvd25fcmVzdWx0cyIpIHsKICAKICAjIGRlZmluZSB0aGUgR08gY2F0ZWdvcmllcyAKICBnb19jYXRlZ29yaWVzIDwtIGMoIkJQIiwgIk1GIiwgIkNDIikKICBhbGxfZGF0YSA8LSBkYXRhLmZyYW1lKCkgICMgaW5pdGlhbGl6ZSBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgYWxsIEdPIHRlcm0gaW5mbwogIAogICMgaGVscGVyIGZ1bmN0aW9uIHVzZWQgdG8gcHJvY2VzcyBmaWxlcyBpbiBhIGdpdmVuIGZvbGRlciBhbmQgZGlyZWN0aW9uICgidXAiIG9yICJkb3duIikKICBwcm9jZXNzX2ZpbGVzIDwtIGZ1bmN0aW9uKGZvbGRlciwgZGlyZWN0aW9uKSB7CiAgICBjYXRlZ29yeV9kYXRhIDwtIGRhdGEuZnJhbWUoKSAgIyBpbml0aWFsaXplIGEgZGF0YSBmcmFtZSBmb3IgdGhlIGNhdGVnb3J5IGluZm8KICAgIAogICAgZm9yIChjYXRlZ29yeSBpbiBnb19jYXRlZ29yaWVzKSB7CiAgICAgICMgYnVpbGQgdGhlIGV4YWN0IGZpbGUgbmFtZSBwYXR0ZXJuIAogICAgICBmaWxlX3BhdHRlcm4gPC0gcGFzdGUwKGRpcmVjdGlvbiwgIl8iLCBjYXRlZ29yeSwgIl8iLCBjb21wYXJpc29uX3BhdHRlcm4sICJfR08uY3N2IikKICAgICAgIyBsaXN0IG91dCBmaWxlcyBtYXRjaGluZyB0aGUgbmFtZSBwYXR0ZXJuIChzaG91bGQgYmUgb25seSBvbmUgcGVyIGNhdGVnb3J5IChCUCwgTUYsIENDKSkKICAgICAgZmlsZXMgPC0gbGlzdC5maWxlcyhmb2xkZXIsIHBhdHRlcm4gPSBmaWxlX3BhdHRlcm4sIGZ1bGwubmFtZXMgPSBUUlVFKQogICAgICAKICAgICAgIyB0YWtlIHRoZSBpbml0aWFsIG1hdGNoIChzaG91bGQgb25seSBiZSBvbmUgYW55d2F5cykKICAgICAgaWYgKGxlbmd0aChmaWxlcykgPiAwKSB7CiAgICAgICAgZGF0YSA8LSByZWFkLmNzdihmaWxlc1sxXSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogICAgICAgIAogICAgICAgICMgZmlsdGVyIHRoZSBkYXRhIG9ubHkga2VlcCBub24tTkEgcC5hZGp1c3QgdmFsdWVzIGFuZCBjcmVhdGUgQ2F0ZWdvcnkgYW5kIFJlZ3VsYXRpb24gY29sdW1ucwogICAgICAgIGRhdGEgPC0gZGF0YSB8PgogICAgICAgICAgZHBseXI6OmZpbHRlcighaXMubmEocC5hZGp1c3QpKSB8PgogICAgICAgICAgZHBseXI6Om11dGF0ZShDYXRlZ29yeSA9IGNhdGVnb3J5LCBSZWd1bGF0aW9uID0gZGlyZWN0aW9uKQogICAgICAgIAogICAgICAgICMgYmluZCB0aGUgZGF0YSBmb3IgYSBjYXRlZ29yeSB0byB0aGUgY2F0ZWdvcnlfZGF0YSBkYXRhIGZyYW1lCiAgICAgICAgY2F0ZWdvcnlfZGF0YSA8LSBkcGx5cjo6YmluZF9yb3dzKGNhdGVnb3J5X2RhdGEsIGRhdGEpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgIyBpZiBubyBmaWxlcyBhcmUgZm91bmQgKGkuZS4gaW5jb3JyZWN0IHBhdHRlcm4gaW5wdXQpLCBwcmludCBhIHdhcm5pbmcgZXJyb3IgCiAgICAgICAgd2FybmluZyhwYXN0ZSgiTm8gbWF0Y2hpbmcgZmlsZSBmb3VuZCBmb3IiLCBkaXJlY3Rpb24sIGNhdGVnb3J5LCAiaW4iLCBmb2xkZXIpKQogICAgICB9CiAgICB9CiAgICByZXR1cm4oY2F0ZWdvcnlfZGF0YSkgICMgcmV0dXJuIHRoZSBjb2xsZWN0ZWQgZGF0YSBmb3IgdGhlIGdpdmVuIGRpcmVjdGlvbgogIH0KICAKICAjIHByb2Nlc3MgYm90aCB0aGUgInVwIiBhbmQgImRvd24iIENTViBmaWxlIHJlc3VsdHMgCiAgdXBfZGF0YSA8LSBwcm9jZXNzX2ZpbGVzKGZpbGUucGF0aChkYXRhX2ZpbGVfcGF0aCwgdXBfcmVndWxhdGVkX2RhdGEpLCAidXAiKQogIGRvd25fZGF0YSA8LSBwcm9jZXNzX2ZpbGVzKGZpbGUucGF0aChkYXRhX2ZpbGVfcGF0aCwgZG93bl9yZWd1bGF0ZWRfZGF0YSksICJkb3duIikKICAKICAjIGNvbWJpbmUgdGhlIHVwLXJlZ3VsYXRlZCBhbmQgZG93bi1yZWd1bGF0ZWQgZGF0YSBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgKGluIHRoZSBmb3JtIG9mIGFsbF9kYXRhKQogIGFsbF9kYXRhIDwtIGRwbHlyOjpiaW5kX3Jvd3ModXBfZGF0YSwgZG93bl9kYXRhKQogIAogICMgaWYgbm8gc2lnbmlmaWNhbnQgKDwgMC4wNSkgR08gdGVybXMgYXJlIGZvdW5kLCBkaXNwbGF5IGVycm9yIAogIGlmIChucm93KGFsbF9kYXRhKSA9PSAwKSB7CiAgICBzdG9wKCJObyBzaWduaWZpY2FudCBHTyB0ZXJtcyBmb3VuZCBmb3IgZ2l2ZW4gY29tcGFyaXNvbiBwYXR0ZXJuLiIpCiAgfQogIAogICMgc2VsZWN0IHRoZSB0b3AgNSBHTyB0ZXJtcyAKICB0b3BfZ29fZGF0YSA8LSBhbGxfZGF0YSB8PgogICAgZ3JvdXBfYnkoQ2F0ZWdvcnkpIHw+CiAgICBhcnJhbmdlKHAuYWRqdXN0KSB8PiAgIyBzb3J0IGJ5IHNtYWxsZXN0IHAuYWRqdXN0IHRvIGdldCB0aGUgbW9zdCBzaWduaWZpY2FudCB0ZXJtcyAoYXNjZW5kaW5nIG9yZGVyKQogICAgc2xpY2VfaGVhZChuID0gdG9wX24pIHw+ICAgIyBzZWxlY3QgdGhlIHRvcCBHTyB0ZXJtcyBwZXIgY2F0ZWdvcnkgKGNob29zZSBob3cgbWFueSB3aGVuIGNhbGxpbmcgdGhlIGZ1bmN0aW9uKQogICAgdW5ncm91cCgpIHw+CiAgICBtdXRhdGUoTGFiZWwgPSBwYXN0ZTAoSUQsICI6ICIsIERlc2NyaXB0aW9uKSkgICMgY3JlYXRlIGEgbGFiZWwgZm9yIGVhY2ggR08gdGVybSAoSUQ6IERlc2NyaXB0aW9uKQogIAogICMgcGxvdCB0aGUgdG9wIEdPIHRlcm1zIAogIGdncGxvdCh0b3BfZ29fZGF0YSwgYWVzKHggPSByZW9yZGVyKExhYmVsLCBwLmFkanVzdCksIHkgPSAtbG9nMTAocC5hZGp1c3QpLCBmaWxsID0gUmVndWxhdGlvbikpICsKICAgIGdlb21fY29sKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjcpKSArICAjIHVzZSBwb3NpdGlvbl9kb2RnZSB0byBwbGFjZSB1cC9kb3duIGJhcnMgc2lkZSBieSBzaWRlCiAgICBjb29yZF9mbGlwKCkgKyAgIyBmbGlwIGNvb3JkaW5hdGVzIHNvIHRoZSBiYXJzIGFyZSBob3Jpem9udGFsCiAgICBmYWNldF9ncmlkKENhdGVnb3J5IH4gLiwgc2NhbGVzID0gImZyZWVfeSIsIHNwYWNlID0gImZyZWUiKSArICAjIGNhdGVnb3JpZXMgZ3JvdXBlZCB0b2dldGhlciB3aXRoIGZyZWUgeS1zY2FsZXMKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInVwIiA9ICJsaWdodGJsdWUiLCAiZG93biIgPSAicGluayIpKSArICAjIHNldCBjb2xvcnMgZm9yIHVwIGFuZCBkb3duIHJlZ3VsYXRpb24gCiAgICBsYWJzKHRpdGxlID0gcGFzdGUodG9wX24sICJNb3N0IFNpZ25pZmljYW50IEdPIFRlcm1zIEFjcm9zcyBPTlQgQ2F0ZWdvcmllcyBmb3IiLCBjb21wYXJpc29uX3BhdHRlcm4pLAogICAgICAgICB4ID0gIkdPIFRlcm0gKElEOiBEZXNjcmlwdGlvbikiLCAKICAgICAgICAgeSA9ICItbG9nMTAocC5hZGp1c3QpIiwgCiAgICAgICAgIGZpbGwgPSAiUmVndWxhdGlvbiIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSkgICMga2VlcCBmYWNldCBsYWJlbHMgaG9yaXpvbnRhbCAoZm9yIGNhdGVnb3JpZXMpCn0KYGBgCgpFeGFtcGxlIHJ1biB1c2luZyB0aGUgY29udHJvbCBncm91cCBjb21wYXJpc29uOgpgYGB7cn0KcGxvdF90b3BfR09fdGVybXMoY29tcGFyaXNvbl9wYXR0ZXJuID0gImNvbnRyb2xfa29fMF92c193dF8wIiwgdG9wX24gPSA1KQpgYGAKCiMgOSkgREVHIFZpc3VhbGl6YXRpb24gClRoZSBmb2xsb3dpbmcgY29kZSBpcyBnb2luZyB0byBleHBsb3JlIHRoZSB2YXJpb3VzIHJlc3VsdHMgc3RlbW1pbmcgZnJvbSBvdXIgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcyB0aHJvdWdoIGEgZmV3IHZpc3VhbGl6YXRpb24gdGVjaG5pcXVlcy4gCgojIyBIZWF0IE1hcCAKKiozLzE4LzI1KioKClVzaW5nIHRoZSBhIGRkcyBvYmplY3QgY29tcGFyaXNvbiBhbmQgaXRzIHJlc3VsdHMgb2JqZWN0IHByb2R1Y2VkIGVhcmxpZXIgaW4gdGhlIHNjcmlwdCwgd2Ugd2lsbCBiZSBtYWtpbmcgYSBoZWF0IG1hcCB0aGF0IGRlbW9uc3RyYXRlcyB0aGUgcGF0dGVybiByZWdhcmRpbmcgdGhlIGhpZ2hlc3QgNTAgYW5kIGxvd2VzdCA1MCBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIGFjY29yZGluZyB0byB0aGVpciBsb2cyRm9sZENoYW5nZSB2YWx1ZS4gVGhpcyBpcyBzaW1wbHkgdG8gdmlzdWFsaXplIHRoZSBwYXR0ZXJuLiAKCkZvciB0aGlzIGluaXRpYWwgZXhhbXBsZSwgd2Ugd2lsbCBiZSB1c2luZyBkZHNfY29udHJvbHMgYW5kIHJlc19kZHNfY29udHJvbHMgd2hpY2ggYXJlIHRoZSBkZHMgb2JqZWN0cyBjb21wYXJpbmcga29fMCB2cy4gd3RfMC4gSSBtYWlubHkgc3RhcnRlZCB3aXRoIHRoaXMgYmVjYXVzZSBpdCBkaWQgbm90IG5lZWQgYW55IGZ1cnRoZXIgY29udHJhc3RpbmcgYnV0IGlmIHdlIHdhbnQgdG8gdGVzdCBtb3JlIHdlIHdvdWxkIGhhdmUgdG8gY29udHJhc3QgYWNjb3JkaW5nbHkgdG8gdGhlIHNhbXBsZXMuIAoKVGhpcyBoZWF0IG1hcCBmaWx0ZXJzIG91dCBmb3IgcGFkaiA8IDAuMDUgYW5kIG9taXRzIHRoZSBOQSB2YWx1ZXMgZm91bmQgaW4gdGhlIHBhZGogY29sdW1uOiAgCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgd2UgYXJlIHVzaW5nIHRoZSByZXN1bHRzIG9mIHRoZSBkZHMgb2JqZWN0IGZvciB0aGUgY29udHJvbCBzYW1wbGVzIChrb18wIHZzLiB3dF8wKSBtYWlubHkgYmVjYXVzZSB3ZSBkaWRuJ3QgaGF2ZSB0byBjb250cmFzdCBhbnl0aGluZyBhbmQgSSB3YXMgdGlyZWQgb2YgY29kaW5nIChkZHMgb2JqZWN0OiBkZHNfY29udHJvbHM7IHJlc3VsdCBkZHMgb2JqZWN0OiByZXNfZGRzX2NvbnRyb2xzKQoKIyBmaWx0ZXIgcmVzdWx0cyB0byBpbmNsdWRlIG9ubHkgZ2VuZXMgd2l0aCBwYWRqIDwgMC4wNSBhbmQgb21pdCB0aG9zZSB3aXRoIGEgcGFkaiA9PSBOQSAKZmlsdGVyZWRfcmVzX2Rkc19jb250cm9scyA8LSByZXNfZGRzX2NvbnRyb2xzWyFpcy5uYShyZXNfZGRzX2NvbnRyb2xzJHBhZGopICYgcmVzX2Rkc19jb250cm9scyRwYWRqIDwgMC4wNSwgXQoKIyBzb3J0IHRoZSByZXN1bHRzIGJ5IGxvZzJGb2xkQ2hhbmdlLCBoaWdoZXN0IHRvIGxvd2VzdCwgYW5kIGxvd2VzdCB0byBoaWdoZXN0Cmxhcmdlc3RfZmMgPC0gaGVhZChmaWx0ZXJlZF9yZXNfZGRzX2NvbnRyb2xzW29yZGVyKGZpbHRlcmVkX3Jlc19kZHNfY29udHJvbHMkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXSwgNTApICMgdG9wIDUwIHVwIHJlZ3VsYXRlZApzbWFsbGVzdF9mYyA8LSBoZWFkKGZpbHRlcmVkX3Jlc19kZHNfY29udHJvbHNbb3JkZXIoZmlsdGVyZWRfcmVzX2Rkc19jb250cm9scyRsb2cyRm9sZENoYW5nZSksIF0sIDUwKSAgIyB0b3AgNTAgZG93biByZWd1bGF0ZWQgCgojIGNvbWJpbmUgdGhlIHR3byBzZXRzIG9mIGZjIHJlc3VsdHMgCmhpZ2hfYW5kX2xvd181MF9nZW5lcyA8LSBjKHJvd25hbWVzKGxhcmdlc3RfZmMpLCByb3duYW1lcyhzbWFsbGVzdF9mYykpCgojIGV4dHJhY3Qgbm9ybWFsaXplZCBjb3VudHMgdXNpbmcgREVTZXEyJ3MgdnN0KCkgZnVuY3Rpb24Kbm9ybWFsaXplZF9kZHNfY29udHJvbHNfY291bnRzIDwtIGFzc2F5KHZzdChkZHNfY29udHJvbHMpKVtoaWdoX2FuZF9sb3dfNTBfZ2VuZXMsIF0KCiMgY3JlYXRlIGEgaGVhdCBtYXAgdXNpbmcgcGhlYXRtYXAoKQpwaGVhdG1hcChub3JtYWxpemVkX2Rkc19jb250cm9sc19jb3VudHMsIAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gVFJVRSwgCiAgICAgICAgIHNjYWxlID0gInJvdyIsIAogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBUUlVFLAogICAgICAgICBmb250c2l6ZSA9IDEwLCAgICAgICAgICAgICMgYWRqdXN0IGZvciBjb2x1bW4gbGFiZWxzCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDYsICAgICAgICAgIyBhZGp1c3QgZm9yIHJvdyBsYWJlbHMKICAgICAgICAgaGVpZ2h0ID0gMzAwLCAgICAgICAgICAgICAjIG1ha2UgdGhlIHBsb3Qgc2lnbmlmaWNhbnRseSBsb25nZXIgCiAgICAgICAgIHdpZHRoID0gMTIsICAgICAgICAgICAgICAgIyBhZGp1c3Qgd2lkdGggYXMgbmVlZGVkCiAgICAgICAgIG1haW4gPSAiSGVhdG1hcCBvZiA1MCBMYXJnZXN0IGFuZCBTbWFsbGVzdCBsb2cyRm9sZENoYW5nZSBHZW5lcyAocGFkaiA8IDAuMDUpIFxuT25seSBDb250cm9sIFNhbXBsZXM6IHd0XzAgdnMua29fMCIpCmBgYAoKVGhpcyBoZWF0IG1hcCBsYWNrcyBmaWx0ZXJpbmcgZm9yIHBhZGogPCAwLjA1IChpLmUuIGluY2x1ZGVzIGV2ZXJ5dGhpbmcpOiAKYGBge3IsIGVjaG8gPSBGQUxTRX0KIyBzb3J0IHRoZSByZXN1bHRzIGJ5IGxvZzJGb2xkQ2hhbmdlLCBoaWdoZXN0IHRvIGxvd2VzdCwgYW5kIGxvd2VzdCB0byBoaWdoZXN0Cmxhcmdlc3RfZmMgPC0gaGVhZChyZXNfZGRzX2NvbnRyb2xzW29yZGVyKHJlc19kZHNfY29udHJvbHMkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXSwgNTApICMgdG9wIDUwIHVwIHJlZ3VsYXRlZApzbWFsbGVzdF9mYyA8LSBoZWFkKHJlc19kZHNfY29udHJvbHNbb3JkZXIocmVzX2Rkc19jb250cm9scyRsb2cyRm9sZENoYW5nZSksIF0sIDUwKSAgIyB0b3AgNTAgZG93biByZWd1bGF0ZWQgCgojIGNvbWJpbmUgdGhlIHR3byBzZXRzIG9mIGZjIHJlc3VsdHMgCmhpZ2hfYW5kX2xvd181MF9nZW5lcyA8LSBjKHJvd25hbWVzKGxhcmdlc3RfZmMpLCByb3duYW1lcyhzbWFsbGVzdF9mYykpCgojIGV4dHJhY3Qgbm9ybWFsaXplZCBjb3VudHMgdXNpbmcgREVTZXEyJ3MgdnN0KCkgZnVuY3Rpb24Kbm9ybWFsaXplZF9kZHNfY29udHJvbHNfY291bnRzIDwtIGFzc2F5KHZzdChkZHNfY29udHJvbHMpKVtoaWdoX2FuZF9sb3dfNTBfZ2VuZXMsIF0KCiMgY3JlYXRlIGEgaGVhdCBtYXAgdXNpbmcgcGhlYXRtYXAoKQpwaGVhdG1hcChub3JtYWxpemVkX2Rkc19jb250cm9sc19jb3VudHMsIAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gVFJVRSwgCiAgICAgICAgIHNjYWxlID0gInJvdyIsIAogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBUUlVFLAogICAgICAgICBmb250c2l6ZSA9IDEwLCAgICAgICAgICAgICMgYWRqdXN0IGZvciBjb2x1bW4gbGFiZWxzCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDYsICAgICAgICAgIyBhZGp1c3QgZm9yIHJvdyBsYWJlbHMKICAgICAgICAgaGVpZ2h0ID0gMzAwLCAgICAgICAgICAgICAjIG1ha2UgdGhlIHBsb3Qgc2lnbmlmaWNhbnRseSBsb25nZXIgCiAgICAgICAgIHdpZHRoID0gMTIsICAgICAgICAgICAgICAgIyBhZGp1c3Qgd2lkdGggYXMgbmVlZGVkCiAgICAgICAgIG1haW4gPSAiSGVhdG1hcCBvZiA1MCBMYXJnZXN0IGFuZCBTbWFsbGVzdCBsb2cyRm9sZENoYW5nZSBHZW5lcyAoTm8gcGFkaiBmaWx0ZXIpIFxuT25seSBDb250cm9sIFNhbXBsZXM6IHd0XzAgdnMuIGtvXzAiKQpgYGAKCioqU2hvdWxkIHdlIGZpbHRlciB0aGUgcGFkaiBvciBsZWF2ZSBpdCBhcyBpcz8gU2hvdWxkIHdlIHVzZSBub3JtYWxpemVkIGNvdW50cz8gSSdtIGFzc3VtaW5nIHllcyEqKgoKIyMgS0VHRyBQYXRod2F5IEFuYWx5c2lzIAoqKjMvMTcvMjUqKgoKVGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgdXRpbGl6ZSBLRUdHIFBhdGh3YXkgQW5hbHlzaXMgdG8gYm90aCBmaW5kIGFuZCB2aXN1YWxpemUgY2VydGFpbiBiaW9sb2dpY2FsIHBhdGh3YXlzIGFuZCBob3cgdGhleSBhcmUgYWZmZWN0ZWQgYnkgdGhlIHVwIGFuZCBkb3duIHJlZ3VsYXRpb24gb2YgY2VydGFpbiBnZW5lcy4gCgpVc2UgdGhlIGZvbGxvd2luZyBwYWNrYWdlcyAoYXMgd2VsbCBhcyB0aGUgb3RoZXJzIHVzZWQgaW4gdGhpcyBzY3JpcHQgc28gZmFyKSB0byBjb25kdWN0IHRoaXMgYW5hbHlzaXMuIApgYGB7cn0KbGlicmFyeShET1NFKQpsaWJyYXJ5KHBhdGh2aWV3KQoKIyBzZWFyY2ggZGF0YWJhc2UgdG8gbWFrZSBzdXJlIG91ciBvcmdhbmlzbSBpcyBhY3R1YWxseSBhdmFpbGFibGUgKG5ld3NmbGFzaDogaXQgaXMpCnNlYXJjaF9rZWdnX29yZ2FuaXNtKCdBcmFiaWRvcHNpcyB0aGFsaWFuYScsIGJ5ID0gJ3NjaWVudGlmaWNfbmFtZScpCmBgYAoKSUdOT1JFIFRIRSBTRUNUSU9OIFRJVExFRCAnQ09OVkVSVCBUQUlSIElEUyBUTyBFTlRSRVogSURTJyBCRUNBVVNFIEFDQ09SRElORyBUTyBUSEUgRk9MTE9XSU5HOgpgYGB7ciwgZWNobyA9IEZBTFNFfQpkYXRhKGtvcmcpCnJpPWtvcmdbLCJrZWdnLmNvZGUiXT09ImF0aCIKa29yZ1tyaSxdCmBgYApXRSBBQ1RVQUxMWSBETyBOT1QgTkVFRCBUSEUgRU5UUkVaSUQgSU4gT1JERVIgVE8gQ09ORFVDVCBLRUdHIFBBVEhXQVkgQU5BTFlTSVMgVVNJTkcgRU5SSUNIS0VHRygpIEFORCBDTFVTVEVSUFJPRklMRVIgRk9SIEFSQUJJRE9QU0lTISAKCiMjIyBDb252ZXJ0IFRBSVIgSURzIHRvIEVOVFJFWiBJRHMgKElHTk9SRSBUSElTKQpUSEUgRk9MTE9XSU5HIENPREUgSVMgU0lNUExZIEZPUiBSRUZFUkVOQ0U6CmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQojIGNob29zZSBhbnkgb2YgdGhlIERlc2VxMiByZXN1bHRpbmcgZGF0YSBzZXRzIChJJ20gdGVzdGluZyBrbm9ja291dC1saW5lIHZzLiB3aWxkLXR5cGUgY29udHJvbHMpCgojIGNvcHkgb3JpZ2luYWwgZGF0YSBmcmFtZSBhbmQgcmVtb3ZlIHRhaWxpbmcgIi4xIiB0byBtYXRjaCBUQUlSIGZvcm1hdCAKZGZfZGRzX2NvbnRyb2xzX2tvX3ZzX3d0X2VudHJleiA8LSBkZl9kZHNfY29udHJvbHNfa25vY2tvdXRfbGluZV92c193aWxkX3R5cGUgfD4KICBtdXRhdGUoR2VuZUlEID0gc3ViKCJcXC5cXGQrJCIsICIiLCBHZW5lSUQpKQoKIyBleHRyYWN0IHRoZSBHZW5lSUQgY29sdW1uCnRhaXJfaWRzX2NvbnRyb2xzIDwtIGRmX2Rkc19jb250cm9sc19rb192c193dF9lbnRyZXokR2VuZUlECgojIGNvbnZlcnQgVEFJUiBJRHMgdG8gRU5UUkVaIElEcwplbnRyZXpfbWFwcGluZ19jb250cm9scyA8LSBzZWxlY3Qob3JnLkF0LnRhaXIuZGIsIGtleXMgPSB0YWlyX2lkc19jb250cm9scywga2V5dHlwZSA9ICJUQUlSIiwgY29sdW1ucyA9ICJFTlRSRVpJRCIpCgojIHdoZW4gbWF0Y2hpbmcgVEFJUiBJRHMgdG8gRU5UUkVaIElEcywgYSB3YXJuaW5nIGRlbm90ZXMgdGhhdCB0aGVyZSBhcmUgbWFueToxIG1hcHBpbmcgaW5zdGFuY2VzIG9mIFRBSVIgdG8gRU5UUkVaIChpLmUuIHZhcmlvdXMgaW5zdGFuY2VzIG9mIHRoZSBzYW1lIFRBSVIgdmFsdWVzIGJlaW5nIG1hdGNoZWQgdG8gdGhlIHNhbWUgRU5UUkVaIHZhbHVlcykuIFRoaXMgaXMgZHVlIHRvIHRyYW5zY3JpcHQgdmFyaWV0eSAoLjEgdnMuIC4zLCBidXQgSSB0aGluayB0aGF0IHNob3VsZCBiZSBmaW5lKS4gSXMgaXQ/IAplbnRyZXpfbWFwcGluZ19jb250cm9scyB8PgogIGRwbHlyOjpjb3VudChUQUlSKSB8PgogIGRwbHlyOjpmaWx0ZXIobiA+IDEpCgojIG1lcmdlIHdpdGggbmV3bHkgbWFkZSBfZW50cmV6IGRhdGEgZnJhbWUgdG8ga2VlcCB0cmFjayBvZiBJRHMKZGZfZGRzX2NvbnRyb2xzX2tvX3ZzX3d0X2VudHJleiA8LSBtZXJnZShkZl9kZHNfY29udHJvbHNfa29fdnNfd3RfZW50cmV6LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnRyZXpfbWFwcGluZ19jb250cm9scywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJHZW5lSUQiLCBieS55ID0gIlRBSVIiLCBhbGwueCA9IFRSVUUpCgojIG1vdmUgdGhlIG5ld2x5IGNyZWF0ZWQgRU5UUkVaSUQgbmV4dCB0byB0aGUgR2VuZUlEIGNvbHVtbiBhbmQgcmVtb3ZlIGR1cGxpY2F0ZXMgCmRmX2Rkc19jb250cm9sc19rb192c193dF9lbnRyZXogPC0gZGZfZGRzX2NvbnRyb2xzX2tvX3ZzX3d0X2VudHJleiB8PgogIGRwbHlyOjpzZWxlY3QoR2VuZUlELCBFTlRSRVpJRCwgZXZlcnl0aGluZygpKSB8PgogIGRpc3RpbmN0KCkgIyBjb2xsYXBzZWQgYSBmZXcgZHVwbGljYXRlIGVudHJpZXMgZHVlIHRvIG1hbnk6MSBtYXBwaW5nIAoKIyBWaWV3KGRmX2Rkc19jb250cm9sc19rb192c193dF9lbnRyZXopCmBgYAoKSUdOT1JFIFRISVMgQVMgV0VMTCEgCmBgYHtyfQojIGFkZCBhIG5ldyBjb2x1bW4gKHJlZ3VsYXRpb24pIHRoYXQgc2lnbmlmaWVzIFVQIG9yIERPV04gYWNjb3JkaW5nIHRvIHRoZSBsb2cyRm9sZENoYW5nZSAKZGZfZGRzX2NvbnRyb2xzX2tvX3ZzX3d0X2VudHJleiA8LSBkZl9kZHNfY29udHJvbHNfa29fdnNfd3RfZW50cmV6IHw+CiAgbXV0YXRlKHJlZ3VsYXRpb24gPSBjYXNlX3doZW4oCiAgICBsb2cyRm9sZENoYW5nZSA+IDAgfiAiVVAiLAogICAgbG9nMkZvbGRDaGFuZ2UgPCAwIH4gIkRPV04iCiAgKSkKCiMgb21pdHMgcm93cyB3aXRoIE5BIHZhbHVlcyBpbiB0aGUgRU5UUkVaSUQgY29sdW1uIApkZl9kZHNfY29udHJvbHNfa29fdnNfd3RfZW50cmV6IDwtIGRmX2Rkc19jb250cm9sc19rb192c193dF9lbnRyZXpbIWlzLm5hKGRmX2Rkc19jb250cm9sc19rb192c193dF9lbnRyZXokRU5UUkVaSUQpLCBdCmBgYAoKIyMjIFByZXBhcmUgR2VuZSBMaXN0IApgYGB7cn0KIyBzZWxlY3Qgd2hpY2hldmVyIERlc2VxMiByZXN1bHRzIHlvdSBsaWtlIChJJ20gdGVzdGluZyBrbm9ja291dC1saW5lIHZzLiB3aWxkLXR5cGUgY29udHJvbHMpCgojIHJlbW92ZSB0YWlsaW5nICIuMSIgdG8gbWF0Y2ggVEFJUiBmb3JtYXQgKHJlbWVtYmVyOiBwYWRqIGlzIGFscmVhZHkgPCAwLjA1IHNvIHdlIGRvbid0IGhhdmUgdG8gZmlsdGVyKQpkZHNfa2VnZ19jb250cm9scyA8LSBkZl9kZHNfY29udHJvbHNfa25vY2tvdXRfbGluZV92c193aWxkX3R5cGUgfD4KICBtdXRhdGUoR2VuZUlEID0gc3ViKCJcXC5cXGQrJCIsICIiLCBHZW5lSUQpKQoKIyBwcmVwYXJlIHRoZSBnZW5lIGxpc3QgZm9yIEtFR0cgYW5hbHlzaXMKZ2VuZUxpc3RfY29udHJvbHMgPC0gZGRzX2tlZ2dfY29udHJvbHMkbG9nMkZvbGRDaGFuZ2UgIyBtYWtlcyB2ZWN0b3Igd2l0aCBsb2cyRm9sZENoYW5nZSB2YWx1ZXMgCm5hbWVzKGdlbmVMaXN0X2NvbnRyb2xzKSA8LSBkZHNfa2VnZ19jb250cm9scyRHZW5lSUQgIyBhZGRzIEdlbmVJRCB0byB0aGUgdmVjdG9yIApgYGAKCiMjIyBLRUdHIFBhdGh3YXkgT3Zlci1SZXByZXNlbnRhdGlvbiBBbmFseXNpcyAKYGBge3J9CiMgcnVuIEtFR0cgZW5yaWNobWVudCBhbmFseXNpcyBvbiB0aGUgZW50aXJlIChib3RoIHVwIGFuZCBkb3duIHJlZ3VsYXRlZCBnZW5lcykgZ2VuZUxpc3RfY29udHJvbHMgYW5kIHByb2R1Y2UgYSBsaXN0IG9mIG92ZXItcmVwcmVzZW50ZWQgcGF0aHdheXMgd2UgY2FuIHRoZW4gdmlzdWFsaXplIGluIHRoZSBuZXh0IHN0ZXAgCmtlZ2dfb3JhX2FuYWx5c2lzX2NvbnRyb2xzIDwtIGVucmljaEtFR0coZ2VuZSA9IG5hbWVzKGdlbmVMaXN0X2NvbnRyb2xzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtICAgICA9ICdhdGgnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCgojIFZpZXcoa2VnZ19vcmFfYW5hbHlzaXNfY29udHJvbHMpCmBgYAoKV2UgY2FuIHRoZW4gdmlzdWFsaXplIG9uZSBvZiB0aGUgcGFydGljdWxhciBwYXRod2F5czogCiAgCiAgLSAqKkknTSBTVFVDSyBIRVJFISBXaHkgaXNuJ3QgaXQgbWFwcGluZyB0byBhbiBJRCB3aGVuIHRoZSBPUkEgYW5hbHlzaXMgY2xlYXJseSBzaG93cyB0aGlzIHNwZWNpZmljIHBhdGh3YXkgaXMgaW4gaXQgYW5kIGFsc28gY2FuIGZpbmQgaXQgdmlhIGJyb3dzZUtFR0coKT8gV0hZIElTTidUIFRISVMgV09SS0lORz8iKioKClJlbW92ZSAnZXZhbCA9IEZBTFNFJyB3aGVuIGl0IHN0YXJ0cyB3b3JraW5nIC4gLiAuCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgdXNlIHBhdGh2aWV3KCkgdG8gdmlzdWFsaXplIHRoZSBwYXRod2F5IApwYXRodmlldyhnZW5lLmRhdGEgICAgID0gZ2VuZUxpc3RfY29udHJvbHMsICMgaW5wdXQgZ2VuZSBsaXN0IChsb2cyRm9sZENoYW5nZSkKICAgICAgICAgcGF0aHdheS5pZCAgICA9ICJhdGgwMDUwMCIsICMgcGF0aHdheSBJRCB3ZSB3YW50IHRvIHZpc3VhbGl6ZQogICAgICAgICBzcGVjaWVzICAgICAgID0gImF0aCIsICAgICAgICAgIyBhcmFiaWRvcHNpcyBzcGVjaWVzIAogICAgICAgICBvdXQuc3VmZml4ICAgID0gInBhdGh3YXkiKQoKIyBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgdG8gdmVyaWZ5IGlmIHRoZSBhY3R1YWwgcGF0aHdheSBldmVuIGV4aXN0cwojIGJyb3dzZUtFR0coa2VnZ19hbmFseXNpc19jb250cm9scywgJ2F0aDAwNTAwJykKYGBgCgojIyMgS0VHRyBQYXRod2F5IEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMgClRIRSBGT0xMT1dJTkcgSVMgTk9UIFdPUktJTkcuLi5vciBpcyBpdDoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyByZW1vdmUgZHVwbGljYXRlcyBieSBrZWVwaW5nIHRoZSBmaXJzdCBvY2N1cnJlbmNlCmdlbmVMaXN0X2NvbnRyb2xzX3VuaXF1ZSA8LSBnZW5lTGlzdF9jb250cm9sc1shZHVwbGljYXRlZChuYW1lcyhnZW5lTGlzdF9jb250cm9scykpXQoKIyBzb3J0IHRoZSBnZW5lIGxpc3QgaW4gZGVjcmVhc2luZyBvcmRlciBvZiBsb2cyRm9sZENoYW5nZQpnZW5lTGlzdF9jb250cm9sc19zb3J0ZWQgPC0gc29ydChnZW5lTGlzdF9jb250cm9sc191bmlxdWUsIGRlY3JlYXNpbmcgPSBUUlVFKQoKIyBOT1QgV09SS0lORyEhIQojIHJ1biBLRUdHIHBhdGh3YXkgZ2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNpcyAKa2VnZ19nc2VhX2NvbnRyb2xzIDwtIGdzZUtFR0coZ2VuZUxpc3QgICAgID0gZ2VuZUxpc3RfY29udHJvbHNfc29ydGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSAgICAgPSAnYXRoJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkgCmBgYAoKIyMgQ29uZmlybWluZyBvciBDb250cmFkaWN0aW5nIEdPIEFuYWx5c2lzIHVzaW5nIHBsb3RDb3VudHMoKQoqKjMvMTgvMjUqKgoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiwgcGxvdF9nZW5lX2NvdW50cygpLCBpcyBhIG1ha2VzaGlmdCBmdW5jdGlvbiB0aGF0IHRha2VzIGFyZ3VlbWVudHMgYW5kIGZlZWRzIHRoZW0gaW50byB0aGUgRGVzZXEyIGJ1aWx0LWluIGZ1bmN0aW9uIGNhbGxlZCBwbG90Q291bnRzKCkuIFRoaXMgYWxsb3dzIHVzIHRvIHZpc3VhbGl6ZSBzcGVjaWZpYyBnZW5lcyBhbmQgdGhlaXIgY291bnRzIGZvdW5kIGluIHNwZWNpZmljIGRkcyBvYmplY3RzIGFuZCBjb250cmFzdHMuIEl0IHRha2VzIGEgZmV3IGFyZ3VlbWVudHM6IAoKICAtIGRkcyA9IHRoZSBkZHMgb2JqZWN0IHlvdSB3aWxsIGJlIHVzaW5nIChub3QgdGhlIHJlc3VsdHMgYnV0IGEgREVTZXFEYXRhU2V0KQogIC0gZ2VuZXMgPSBvbmUgb3IgbW9yZSBnZW5lcyAoanVzdCBtYWtlIHN1cmUgdG8gdXNlIGEgbGlzdCBmb3IgbXVsdGlwbGUpIG9mIHlvdXIgY2hvb3NpbmcgZm91bmQgaW4gdGhlIGRkcwogIC0gaW50Z3JvdXAgPSBhIHNwZWNpZmljIGNvbHVtbiBvciBjb25kaXRpb24gdGhhdCB3YXMgZm91bmQgaW4gY29sRGF0YSB0aGF0IHdlIHdhbnQgdG8gZGlmZmVyZW50aWF0ZSBieSAKICAtIGZpbHRlcl9ncm91cHMgPSB0aGUgc3BlY2lmaWMgZ3JvdXBzIGZvdW5kIHdpdGhpbiBpbnRncm91cCB0aGF0IHlvdSB3aXNoIHRvIGNvbnRyYXN0IAogIApUaGVzZSBwYXJhbWV0ZXJzIGFsbG93IHVzIHRvIHVzZSB0aGUgZmV3IGRkcyBvYmplY3RzIG1hZGUgZWFybGllciBpbiB0aGlzIHNjcmlwdCBhbmQgc3RpbCBjb250cmFzdCBzcGVjaWZpYyBzYW1wbGVzIGFjY29yZGluZ2x5LiAKClRoZSBmdW5jdGlvbiBjYW4gdGFrZSBhIHNpbmdsZSBnZW5lIG9yIGEgbGlzdCBvZiBnZW5lcyBhbmQgd2lsbCBzaW1wbHkgZmFjZXQgd3JhcCBpdCBzbyB0aGF0IHdlIGNhbiBhdm9pZCBtYWtpbmcgYSB0b24gb2YgdW5uZWNlc3NhcnkgcGxvdHMgZm9yIHRoZSBzYW1lIGNvbnRyYXN0IGNvbmRpdGlvbnMgYW5kIHZhcnlpbmcgZ2VuZXMuIEkgd291bGQgb25seSBzdWdnZXN0IHRvIGRvIDIgb3IgMyBnZW5lcyBhdCBhIHRpbWUuIAoKICAtIE5vdGU6IGJlY2F1c2Ugb2YgdGhlIHdheSB0aGUgeC1heGlzIGFuZCB0aGUgcG9pbnRzIGFyZSBoYW5kbGVkLCB0aGUgcGxvdCBvZiBhIHNpbmdsZSBnZW5lIG1ha2UgbG9vayBzbGlnaHRseSBkaWZmZXJlbnQKICB0byBpdHMgdmVyc2lvbiB3aXRoaW4gYSBmYWNldHRlZCBwbG90LiBUaGlzIGlzIHNpbXBseSB0aGUgZWZmZWN0IG9mIG1vdmluZyB0aGUgcG9pbnRzIGluIGRpZmZlcmVudCBwb3NpdGlvbnMgb24gdGhlCiAgeC1heGlzLCBzb21ldGhpbmcgdGhhdCBhY3R1YWxseSBoYXMgbm8gYmVhcmluZyBvbiB0aGUgeS1heGlzIHBvc2l0aW9ucyBhbmQgdGh1cyByZXRhaW5zIHRoZSBpbnRlZ3JpdHkgb2YgdGhlIGNvdW50IGFuZAogIHBsb3R0aW5nIHN0cnVjdHVyZS4gQXQgYSBxdWljayBnbGFuY2UsIGl0IG1heSBzZWVtIGEgYml0IG9mZiBidXQgZG9uJ3Qgc3RyZXNzLCBpdCBpcyB0aGUgZXhhY3Qgc2FtZTsgZG9uJ3QKICBiZWxpZXZlIG1lPyBDb21wYXJlIGFuZCBjb250cmFzdCEgCgpJbiB0aGlzIHBhcnRpY3VsYXIgc2NyaXB0LCB0aGUgY2hvaWNlIG9mIHJlLWxldmVsZWQgZGRzIG9iamVjdHMgd2UgaGF2ZSBhcmU6CgogIC0gZGRzX2NvbnRyb2xzIChjb250cm9sIHNhbXBsZXMpCiAgLSBkZHNfdHJlYXRtZW50ICh0cmVhdG1lbnQgdnMuIHRyZWF0bWVudCBzYW1wbGVzIG9mIGJvdGggd2lsZC10eXBlIGFuZCBrbm9ja291dC1saW5lKQogIC0gZGRzX2tvIChvbmx5IGtub2Nrb3V0LWxpbmUgdHJlYXRtZW50IHNhbXBsZXMpCiAgLSBkZHNfd3QgKG9ubHkgd2lsZC10eXBlIHRyZWF0bWVudCBzYW1wbGVzKQoKVGhleSBhcmUgYWxzbyBmb3VuZCBpbiB0aGUgdGl0bGUgb2YgZWFjaCBwbG90IGRpc3Rpbmd1aXNoaW5nIHRoZSBwbG90cyBmcm9tIG9uZSBhbm90aGVyIChlc3BlY2lhbGx5IGZvciBkZHNfa28gYW5kIGRkc193dCB3aGljaCBvbmx5IHVzZSBudW1iZXJzIGZvciB0aGVpciB4LWF4aXMpLiAKICAKVXNlIGFueSBvZiB0aGVzZSBhbmQgc3BlY2lmeSBhY2NvcmRpbmcgdG8gYSBwYXJ0aWN1bGFyIGdlbmUsIGludGdyb3VwIHdoaWNoIGlzIGEgY29sRGF0YSBjb2x1bW4gdXNlZCBpbiB0aGUgb3JpZ2luYWwgY29udHJhc3QgKGdyb3VwIHZzLiB0cmVhdG1lbnQgdnMuIGdlbm90eXBlKSwgYW5kIHNwZWNpZmljIHNhbXBsZXMgZm9yIGZpbHRlcl9ncm91cHMgKHd0XzAgdnMuIGtvXzApLgoKQ29tcGFyZSB0aGUgY2x1c3RlcmluZyBvZiBwb2ludHMgdG8gdGhlIGxvZzJGb2xkQ2hhbmdlIG9mIHlvdXIgY2hvc2VuIGdlbmUocykuIFJlbWVtYmVyIHRoYXQgdGhlIGxvZzJGb2xkQ2hhbmdlIHJlcHJlc2VudHMgdXAgb3IgZG93biByZWd1bGF0aW9uIHJlbGF0aXZlIHRvIHRoZSByZWZlcmVuY2UgbGV2ZWwgd2hpY2ggaGFzIGJlZW4gc2V0IGFzIHRoZSB3aWxkLXR5cGUgY29udHJvbCwgd2lsZC10eXBlIHZhcmlhbnQsIG9yIGtub2Nrb3V0LWxpbmUgY29udHJvbCB3aGVyZSBhcHByb3ByaWF0ZS4gVGhlbiwgcGh5c2ljYWxseSBsb29rIGF0IHRoZSBsb2cyRm9sZENoYW5nZSB2YWx1ZSBhbmQgaWYsIGZvciBleGFtcGxlLCBpdCBpcyBuZWdhdGl2ZSwgbWFrZSBzdXJlIGl0cyBjbHVzdGVyaW5nIGlzIGxvd2VyIHRoYW4gdGhlIHJlZmVyZW5jZSBsZXZlbCBjbHVzdGVyaW5nIG9uIHRoZSBncmFwaC4gVmljZSB2ZXJzYSBpZiBwb3NpdGl2ZS4gSWYgYm90aCBzZXRzIG9mIGNsdXN0ZXJpbmcgYXJlIGNsb3NlIHRvZ2V0aGVyLCB0aGF0IGlzIGluZGljYXRpdmUgb2YgYSBmYWlybHkgc21hbGwgcGFkaiB2YWx1ZS4gV2UgYXJlIGp1c3QgdHJ5aW5nIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBsb2cyRm9sZENoYW5nZSBpcyBhY2N1cmF0ZWx5IGRlbW9uc3RyYXRpbmcgdXAgb3IgZG93biByZWd1bGF0aW9uIHZpYSB0aGUgbm9ybWFsaXplZCBjb3VudHMuIAoKICAtIGRvIDIgb3IgMyBnZW5lcyBwZXIgZGRzIG9iamVjdCBhbmQgY29udHJhc3QgCiAgCkxhc3RseSwgSSd2ZSBpbmNsdWRlZCBhIHJlZCBkb3QgdGhhdCBpcyByZXByZXNlbnRhdGl2ZSBvZiB0aGUgbWVhbiB2YWx1ZSBiZXR3ZWVuIHRoZSB0cmlwbGljYXRlIGZvciBlYWNoIHNhbXBsZS4gWW91IGNvdWxkIHJlbW92ZSB0aGF0IGlmIHlvdSdkIGxpa2UuIAoKKipTaG91bGQgSSB1c2Ugbm9ybWFsaXplZCBjb3VudHMgbGlrZSBwbG90Q291bnRzKCkgbm9ybWFsbHkgZG9lcyBvciBzaG91bGQgSSB1c2UgdGhlIHJhdyBjb3VudHMgYXMgYWNjb21wbGlzaGVkIGJ5IGNoYW5naW5nOiBwbG90X2RhdGEgPC0gcGxvdENvdW50cyhkZHMsIGdlbmUgPSAiQVQxRzAyMjgwLjEiLCBpbnRncm91cCA9ICJncm91cCIsIHJldHVybkRhdGEgPSBUUlVFLCBub3JtYWxpemVkID0gRkFMU0UpKioKYGBge3J9CnBsb3RfZ2VuZV9jb3VudHMgPC0gZnVuY3Rpb24oZGRzLCBnZW5lcywgaW50Z3JvdXAsIGZpbHRlcl9ncm91cHMpIHsKICAjIHB1bGwgbmFtZSBvZiB0aGUgZGRzIG9iamVjdAogIGRkc19uYW1lIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShkZHMpKQogIAogICMgZXh0cmFjdCBwbG90IGRhdGEgZm9yIG11bHRpcGxlIGdlbmVzCiAgcGxvdF9kYXRhX2xpc3QgPC0gbGFwcGx5KGdlbmVzLCBmdW5jdGlvbihnZW5lKSB7CiAgICBwbG90X2RhdGEgPC0gcGxvdENvdW50cyhkZHMsIGdlbmUgPSBnZW5lLCBpbnRncm91cCA9IGludGdyb3VwLCByZXR1cm5EYXRhID0gVFJVRSkKICAgIHBsb3RfZGF0YSRnZW5lIDwtIGdlbmUgICMgQWRkIGdlbmUgaWRlbnRpZmllcgogICAgcGxvdF9kYXRhCiAgfSkKICAKICAjIGNvbWJpbmUgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCiAgcGxvdF9kYXRhX2NvbWJpbmVkIDwtIGJpbmRfcm93cyhwbG90X2RhdGFfbGlzdCkKICAKICAjIGZpbHRlciBiYXNlZCBvbiBzcGVjaWZpZWQgZ3JvdXBzCiAgcGxvdF9kYXRhX2ZpbHRlcmVkIDwtIHBsb3RfZGF0YV9jb21iaW5lZCB8PgogICAgZmlsdGVyKC5kYXRhW1tpbnRncm91cF1dICVpbiUgZmlsdGVyX2dyb3VwcykKCiAgIyBnZW5lcmF0ZSBwbG90IHRpdGxlIGR5bmFtaWNhbGx5CiAgcGxvdF90aXRsZSA8LSBwYXN0ZSgiR2VuZSBDb3VudHMgZm9yIiwgcGFzdGUoZmlsdGVyX2dyb3VwcywgY29sbGFwc2UgPSAiIHZzLiAiKSwKICAgICAgICAgICAgICAgICAgICAgICJcbkRhdGEgZnJvbToiLCBkZHNfbmFtZSkKICAKICAjIGNyZWF0ZSB0aGUgcGxvdCB3aXRoIGZhY2V0X3dyYXAKICBnZ3Bsb3QocGxvdF9kYXRhX2ZpbHRlcmVkLCBhZXNfc3RyaW5nKHggPSBpbnRncm91cCwgeSA9ICJjb3VudCIpKSArCiAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgc2l6ZSA9IDMsIGFscGhhID0gMC42KSArCiAgICBzdGF0X3N1bW1hcnkoZnVuID0gIm1lYW4iLCBnZW9tID0gInBvaW50Iiwgc2l6ZSA9IDQsIGNvbG9yID0gInJlZCIpICsKICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsKICAgIGxhYnModGl0bGUgPSBwbG90X3RpdGxlLCB5ID0gIk5vcm1hbGl6ZWQgQ291bnRzIiwgeCA9ICJDb25kaXRpb24iKSArCiAgICBmYWNldF93cmFwKH5nZW5lLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgdGhlbWUoCiAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJsaWdodGdyYXkiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjUpLCAgIyBIaWdobGlnaHQgZmFjZXQgbGFiZWxzCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSwgICMgTWFrZSBmYWNldCBsYWJlbHMgYm9sZAogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAxKSAgIyBBZGQgYm9yZGVycyB0byBwYW5lbHMKICAgICkKfQoKIyBleGFtcGxlIHdoZW4gdXNpbmcgYSBzaW5nbGUgZ2VuZSBmcm9tIGRkc190cmVhdG1lbnQgKGtvXzIwIHZzLiB3dF8yMCkKcGxvdF9nZW5lX2NvdW50cyhkZHNfdHJlYXRtZW50LCBnZW5lcyA9ICJBVDFHMTkzNTAuNCIsIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gImdyb3VwIiwgZmlsdGVyX2dyb3VwcyA9IGMoImtvXzIwIiwgInd0XzIwIikpCgoKIyBleGFtcGxlIHdoZW4gdXNpbmcgbXVsdGlwbGUgZ2VuZXMgZnJvbSBkZHNfdHJlYXRtZW50IChrb18yMCB2cy4gd3RfMjApCnBsb3RfZ2VuZV9jb3VudHMoZGRzX3RyZWF0bWVudCwgZ2VuZXMgPSBjKCJBVDFHMDIyODAuMSIsICJBVDFHMTkzNTAuNCIpLCAKICAgICAgICAgICAgICAgICBpbnRncm91cCA9ICJncm91cCIsIGZpbHRlcl9ncm91cHMgPSBjKCJrb18yMCIsICJ3dF8yMCIpKQpgYGAKClNpbmNlIHdlJ3ZlIGFscmVhZHkgZG9uZSAyIGdlbmVzIGZvciBrb18yMCB2cy4gd3RfMjAgYWJvdmUsIGxldCdzIGNvbnRpbnVlIGFuZCBkbyBhdCBsZWFzdCAyIGdlbmVzIGZvciBlYWNoIGNvbnRyYXN0IHdlJ3ZlIGRvbmUgdGh1cyBmYXIuIAoKVGhlIHJlbWFuaW5nIEtPIFRyZWF0bWVudCB2cy4gV1QgVHJlYXRtZW50IGNvbnRyYXN0czogCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgZGRzX3RyZWF0bWVudDoga29fNDAgdnMuIHd0XzQwIApwbG90X2dlbmVfY291bnRzKGRkc190cmVhdG1lbnQsIGdlbmVzID0gYygiQVQxRzE4ODcwLjEiLCAiQVQxRzIwNDgwLjEiKSwgCiAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSAiZ3JvdXAiLCBmaWx0ZXJfZ3JvdXBzID0gYygia29fNDAiLCAid3RfNDAiKSkKCiMgZGRzX3RyZWF0bWVudDoga29fNjAgdnMuIHd0XzYwCnBsb3RfZ2VuZV9jb3VudHMoZGRzX3RyZWF0bWVudCwgZ2VuZXMgPSBjKCJBVDFHMjgyMzAuMSIsICJBVDFHMTUwMTAuMSIpLCAKICAgICAgICAgICAgICAgICBpbnRncm91cCA9ICJncm91cCIsIGZpbHRlcl9ncm91cHMgPSBjKCJrb182MCIsICJ3dF82MCIpKQoKIyBkZHNfdHJlYXRtZW50OiBrb184MCB2cy4gd3RfODAKcGxvdF9nZW5lX2NvdW50cyhkZHNfdHJlYXRtZW50LCBnZW5lcyA9IGMoIkFUMUc2OTU3Mi4xIiwgIkFUMkcxNTAyMC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gImdyb3VwIiwgZmlsdGVyX2dyb3VwcyA9IGMoImtvXzgwIiwgInd0XzgwIikpCmBgYAoKT25seSBDb250cm9sczogCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgZGRzX2NvbnRyb2xzOiBrbm9ja291dF9saW5lIHZzLiB3aWxkX3R5cGUgKGtvXzAgdnMuIHd0XzApCnBsb3RfZ2VuZV9jb3VudHMoZGRzX2NvbnRyb2xzLCBnZW5lcyA9IGMoIkFUMUcwMTM5MC4xIiwgIkFUMUcwMjY0MC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gImdlbm90eXBlIiwgZmlsdGVyX2dyb3VwcyA9IGMoImtub2Nrb3V0X2xpbmUiLCAid2lsZF90eXBlIikpCmBgYAoKYGBge3J9CiMgY29kZSBmb3IgaWYgeW91IGV2ZXIgd2FudCB0byB2aWV3IHRoZSBub3JtYWxpemVkIGNvdW50cyB1c2VkIGZvciBwbG90dGluZyB0aGUgZ3JhcGggb2YgYSBzcGVjaWZpYyBnZW5lIGFuZCBjb21wYXJlIHRvIHBsb3Q7IEkndmUgY29tcGFyZWQgYW5kIGl0IGhhcyBsb29rZWQgZ3JlYXQgc28gZmFyCmNvdW50cyhkZHNfY29udHJvbHMsIG5vcm1hbGl6ZWQgPSBUUlVFKVsiQVQxRzAyNjQwLjEiLCBdICMgc3BlY2lmaWMgdG8gYSBwYXJ0aWN1bGFyIGdlbmUgCmBgYAoKT25seSBXaWxkLVR5cGUgU2FtcGxlczogCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgZGRzX3d0OiB3dF8wIHZzLiB3dF8yMCAKcGxvdF9nZW5lX2NvdW50cyhkZHNfd3QsIGdlbmVzID0gYygiQVQxRzAxNTYwLjEiLCAiQVQxRzAyNTIwLjEiKSwgCiAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSAidHJlYXRtZW50IiwgZmlsdGVyX2dyb3VwcyA9IGMoIjIwIiwgIjAiKSkKCiMgZGRzX3d0OiB3dF8wIHZzLiB3dF80MApwbG90X2dlbmVfY291bnRzKGRkc193dCwgZ2VuZXMgPSBjKCJBVDFHMDE3NTAuMSIsICJBVDFHMDIwNjUuMSIpLCAKICAgICAgICAgICAgICAgICBpbnRncm91cCA9ICJ0cmVhdG1lbnQiLCBmaWx0ZXJfZ3JvdXBzID0gYygiNDAiLCAiMCIpKQoKIyBkZHNfd3Q6IHd0XzAgdnMuIHd0XzYwCnBsb3RfZ2VuZV9jb3VudHMoZGRzX3d0LCBnZW5lcyA9IGMoIkFUMUcwMjY0MC4xIiwgIkFUMUcwNDc3MC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCI2MCIsICIwIikpCgojIGRkc193dDogd3RfMCB2cy4gd3RfODAgCnBsb3RfZ2VuZV9jb3VudHMoZGRzX3d0LCBnZW5lcyA9IGMoIkFUMUcwMTUyMC4xIiwgIkFUMUcwMzA5MC4yIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCI4MCIsICIwIikpCmBgYAoKT25seSBLbm9ja291dC1MaW5lIFNhbXBsZXM6IApgYGB7ciwgZWNobyA9IEZBTFNFfQojIGRkc19rbzoga29fMCB2cy4ga29fMjAgCnBsb3RfZ2VuZV9jb3VudHMoZGRzX2tvLCBnZW5lcyA9IGMoIkFUMUcxMjU2MC4xIiwgIkFUMUcxNDQ0MC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCIyMCIsICIwIikpCgojIGRkc19rbzoga29fMCB2cy4ga29fNDAgCnBsb3RfZ2VuZV9jb3VudHMoZGRzX2tvLCBnZW5lcyA9IGMoIkFUMUcwMjY2MC4xIiwgIkFUMUcwMzcwMC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCI0MCIsICIwIikpCgojIGRkc19rbzoga29fMCB2cy4ga29fNjAgCnBsb3RfZ2VuZV9jb3VudHMoZGRzX2tvLCBnZW5lcyA9IGMoIkFUMUcwMTc1MC4xIiwgIkFUMUcwMjYyMC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCI2MCIsICIwIikpCgojIGRkc19rbzoga29fMCB2cy4ga29fODAgCnBsb3RfZ2VuZV9jb3VudHMoZGRzX2tvLCBnZW5lcyA9IGMoIkFUMUcwNTgzNS4xIiwgIkFUMUcwNzUwMC4xIiksIAogICAgICAgICAgICAgICAgIGludGdyb3VwID0gInRyZWF0bWVudCIsIGZpbHRlcl9ncm91cHMgPSBjKCI4MCIsICIwIikpCmBgYAo=